import ForgejoKit
import SwiftUI

// swiftlint:disable:next type_body_length
struct IssueDetailView: View {
    @State private var repository: Repository
    let issueNumber: Int
    @State private var authService: AuthenticationService
    @State private var issue: Issue?
    @State private var comments: [IssueComment] = []
    @State private var isLoading = true
    @State private var errorMessage: String?
    @State private var showError = false
    @State private var showEditSheet = false
    @State private var isTogglingState = false
    @State private var showCommentSheet = false
    @State private var showActionsExpanded = false

    private let issueService: IssueService?
    private let repositoryService: RepositoryService?

    init(repository: Repository, issueNumber: Int, authService: AuthenticationService) {
        self.repository = repository
        self.issueNumber = issueNumber
        self.authService = authService
        issueService = authService.client.map { IssueService(client: $0) }
        repositoryService = authService.client.map { RepositoryService(client: $0) }
    }

    private var owner: String {
        repository.owner
    }

    private var repo: String {
        repository.repoName
    }

    private var hasPushPermission: Bool {
        repository.permissions?.push == true || repository.permissions?.admin == true
    }

    private var isAuthor: Bool {
        guard let currentUser = authService.currentUser?.login else { return false }
        return issue?.user.login == currentUser
    }

    private var canEditOrClose: Bool {
        hasPushPermission || isAuthor
    }

    var body: some View {
        ZStack(alignment: .bottomTrailing) {
            Group {
                if isLoading, issue == nil {
                    ProgressView()
                } else if let issue {
                    List {
                        // Header
                        Section {
                            VStack(alignment: .leading, spacing: 10) {
                                Text(issue.title)
                                    .font(.title3)
                                    .fontWeight(.bold)
                                    .accessibilityIdentifier("issue-detail-title")

                                HStack(spacing: 8) {
                                    HStack(spacing: 4) {
                                        Image(
                                            systemName: issue.stateValue == .open
                                                ? "circle.circle" : "checkmark.circle.fill",
                                        )
                                        Text(issue.stateValue == .open ? "Open" : "Closed")
                                    }
                                    .font(.caption)
                                    .fontWeight(.medium)
                                    .padding(.horizontal, 8)
                                    .padding(.vertical, 4)
                                    .foregroundStyle(.white)
                                    .glassEffect(.regular.tint(issue.stateValue == .open ? .green : .purple))

                                    Text("#\(issue.number)")
                                        .font(.subheadline)
                                        .foregroundStyle(.secondary)
                                }

                                Text("\(issue.user.login) opened \(formatRelativeDate(issue.createdAt))")
                                    .font(.subheadline)
                                    .foregroundStyle(.secondary)

                                if !issue.labels.isEmpty {
                                    FlowLayout(spacing: 4) {
                                        ForEach(issue.labels) { label in
                                            IssueLabelView(label: label)
                                        }
                                    }
                                }
                            }
                            .padding(.vertical, 4)
                        }
                        .listRowBackground(
                            (issue.stateValue == .open ? Color.green : Color.purple).opacity(0.08),
                        )

                        if let milestone = issue.milestone {
                            MilestoneDisplaySection(milestone: milestone)
                        }

                        if let assignees = issue.assignees, !assignees.isEmpty {
                            AssigneesDisplaySection(assignees: assignees)
                        }

                        // Body
                        if let body = issue.body, !body.isEmpty {
                            Section("Description") {
                                MarkdownPreview(text: body)
                                    .padding(.vertical, 4)
                            }
                        }

                        // Attachments
                        if let assets = issue.assets, !assets.isEmpty {
                            Section("Attachments") {
                                AttachmentGallery(attachments: assets)
                                    .padding(.vertical, 4)
                            }
                        }

                        // Comments
                        if !comments.isEmpty {
                            Section("Comments (\(comments.count))") {
                                ForEach(comments) { comment in
                                    CommentView(
                                        comment: comment,
                                        currentUsername: authService.currentUser?.login,
                                    ) { newBody in
                                        await editComment(commentId: comment.id, body: newBody)
                                    }
                                }
                            }
                        }

                        // Spacer so content isn't hidden behind floating bar
                        Section {} footer: {
                            Spacer().frame(height: 60)
                        }
                    }
                    .listStyle(.insetGrouped)
                }
            }

            // Floating glass action cluster
            if let currentIssue = issue {
                ExpandableActionMenu(isExpanded: $showActionsExpanded) {
                    Button { showCommentSheet = true } label: {
                        Label("Comment", systemImage: "text.bubble")
                    }
                    .buttonStyle(.glassProminent)
                    .transition(.move(edge: .bottom).combined(with: .opacity))
                    .accessibilityIdentifier("issue-comment-button")

                    if canEditOrClose {
                        Button { showEditSheet = true } label: {
                            Label("Edit", systemImage: "pencil")
                        }
                        .buttonStyle(.glassProminent)
                        .transition(.move(edge: .bottom).combined(with: .opacity))
                        .accessibilityIdentifier("issue-edit-button")

                        Button { Task { await toggleIssueState() } } label: {
                            if isTogglingState {
                                ProgressView()
                                    .controlSize(.small)
                            } else {
                                Label(
                                    currentIssue.stateValue == .open ? "Close" : "Reopen",
                                    systemImage: currentIssue.stateValue == .open
                                        ? "xmark.circle" : "arrow.uturn.left.circle",
                                )
                            }
                        }
                        .buttonStyle(.glassProminent)
                        .tint(currentIssue.stateValue == .open ? .purple : .green)
                        .disabled(isTogglingState)
                        .transition(.move(edge: .bottom).combined(with: .opacity))
                        .accessibilityIdentifier("issue-toggle-state")
                    }
                }
            }
        }
        .navigationTitle("#\(issueNumber)")
        .navigationBarTitleDisplayMode(.inline)
        .sheet(isPresented: $showEditSheet) {
            if let issue {
                IssueEditView(
                    repository: repository,
                    issue: issue,
                    authService: authService,
                    onUploadImage: uploadIssueImage,
                ) { updatedIssue in
                    self.issue = updatedIssue
                }
            }
        }
        .sheet(isPresented: $showCommentSheet) {
            CommentSheet(users: [], onUploadImage: uploadIssueImage) { body in
                guard let issueService else { throw URLError(.userAuthenticationRequired) }
                let comment = try await issueService.createComment(
                    owner: owner,
                    repo: repo,
                    index: issueNumber,
                    body: body,
                )
                comments.append(comment)
            }
        }
        .task {
            await loadData()
        }
        .errorAlert(message: $errorMessage, isPresented: $showError)
    }

    private func uploadIssueImage(data: Data, fileName: String, mimeType: String) async throws -> String {
        guard let issueService else { throw URLError(.userAuthenticationRequired) }
        let attachment = try await issueService.uploadIssueAttachment(
            owner: owner, repo: repo, index: issueNumber,
            fileData: data, fileName: fileName, mimeType: mimeType,
        )
        return attachmentMarkdown(for: attachment)
    }

    private func loadData() async {
        guard let issueService else { return }
        isLoading = true
        do {
            async let fetchedIssue = issueService.fetchIssue(owner: owner, repo: repo, index: issueNumber)
            async let fetchedComments = issueService.fetchComments(owner: owner, repo: repo, index: issueNumber)
            let loadedIssue = try await fetchedIssue
            let loadedComments = try await fetchedComments
            try Task.checkCancellation()
            issue = loadedIssue
            comments = loadedComments
            if let fullRepo = try? await repositoryService?.fetchRepository(owner: owner, repo: repo) {
                repository = fullRepo
            }
        } catch is CancellationError {
            // Ignore cancellation
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }
        isLoading = false
    }

    private func toggleIssueState() async {
        guard let issueService, let issue else { return }
        isTogglingState = true

        do {
            let newState = issue.stateValue == .open ? IssueState.closed.rawValue : IssueState.open.rawValue
            let updated = try await issueService.editIssue(
                owner: owner,
                repo: repo,
                index: issueNumber,
                title: nil,
                body: nil,
                state: newState,
            )
            self.issue = updated
            NotificationCenter.default.post(name: .issuesDidChange, object: nil)
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }

        isTogglingState = false
    }

    private func editComment(commentId: Int, body: String) async -> Bool {
        guard let issueService else { return false }
        do {
            let updated = try await issueService.editComment(
                owner: owner,
                repo: repo,
                commentId: commentId,
                body: body,
            )
            if let index = comments.firstIndex(where: { $0.id == commentId }) {
                comments[index] = updated
            }
            return true
        } catch {
            errorMessage = error.localizedDescription
            showError = true
            return false
        }
    }

    #if DEBUG
        init(preview _: Void, repository: Repository, issueNumber: Int, authService: AuthenticationService,
             issue: Issue, comments: [IssueComment] = [])
        {
            self.repository = repository
            self.issueNumber = issueNumber
            self.authService = authService
            issueService = nil
            repositoryService = nil
            _issue = State(initialValue: issue)
            _comments = State(initialValue: comments)
            _isLoading = State(initialValue: false)
        }
    #endif
}

#if DEBUG
    #Preview {
        NavigationStack {
            IssueDetailView(
                preview: (),
                repository: .preview,
                issueNumber: 1,
                authService: .previewDefault,
                issue: .preview,
                comments: [.preview],
            )
        }
    }
#endif
