import ForgejoKit
import SwiftUI

struct PullRequestCreateView: View {
    let repository: Repository
    @State private var authService: AuthenticationService
    @State private var title = ""
    @State private var bodyText = ""
    @State private var headBranch = ""
    @State private var baseBranch = ""
    @State private var branches: [Branch] = []
    @State private var isLoadingBranches = true
    @State private var isSubmitting = false
    @State private var errorMessage: String?
    @State private var showError = false
    @State private var availableLabels: [IssueLabel] = []
    @State private var availableMilestones: [IssueMilestone] = []
    @State private var availableAssignees: [User] = []
    @State private var selectedLabelIDs: Set<Int> = []
    @State private var selectedMilestoneID: Int?
    @State private var selectedAssigneeLogins: Set<String> = []
    @State private var selectedReviewerLogins: Set<String> = []
    @State private var createdPRNumber: Int?
    @Environment(\.dismiss) private var dismiss

    private let prService: PullRequestService?
    private let repositoryService: RepositoryService?
    private let onCreated: () -> Void
    private let embeddedInNavigation: Bool

    init(
        repository: Repository,
        authService: AuthenticationService,
        embeddedInNavigation: Bool = false,
        onCreated: @escaping () -> Void,
    ) {
        self.repository = repository
        self.authService = authService
        prService = authService.client.map { PullRequestService(client: $0) }
        repositoryService = authService.client.map { RepositoryService(client: $0) }
        self.onCreated = onCreated
        self.embeddedInNavigation = embeddedInNavigation
        _baseBranch = State(initialValue: repository.defaultBranch ?? "main")
    }

    private var owner: String {
        repository.owner
    }

    private var repo: String {
        repository.repoName
    }

    private var canCreate: Bool {
        !title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
            && !headBranch.isEmpty
            && !baseBranch.isEmpty
            && headBranch != baseBranch
            && !isSubmitting
    }

    var body: some View {
        if embeddedInNavigation {
            formContent
        } else {
            NavigationStack {
                formContent
            }
        }
    }

    private var formContent: some View {
        Form {
            Section("Branches") {
                if isLoadingBranches {
                    HStack {
                        Spacer()
                        ProgressView()
                        Spacer()
                    }
                } else {
                    Picker("Head", selection: $headBranch) {
                        Text("Select branch").tag("")
                        ForEach(branches) { branch in
                            Text(branch.name).tag(branch.name)
                        }
                    }

                    Picker("Base", selection: $baseBranch) {
                        ForEach(branches) { branch in
                            Text(branch.name).tag(branch.name)
                        }
                    }

                    if !headBranch.isEmpty, headBranch == baseBranch {
                        Label("Head and base branches must be different", systemImage: "exclamationmark.triangle")
                            .font(.caption)
                            .foregroundStyle(.orange)
                    }
                }
            }

            Section {
                TextField("Title", text: $title)
                    .accessibilityIdentifier("pr-create-title-field")
            }

            DescriptionEditorSection(text: $bodyText)

            LabelPickerSection(
                availableLabels: availableLabels,
                selectedLabelIDs: $selectedLabelIDs,
            )
            MilestonePickerSection(
                availableMilestones: availableMilestones,
                selectedMilestoneID: $selectedMilestoneID,
            )
            UserPickerSection(
                title: "Assignees",
                availableUsers: availableAssignees,
                selectedLogins: $selectedAssigneeLogins,
            )
            UserPickerSection(
                title: "Reviewers",
                availableUsers: availableAssignees,
                selectedLogins: $selectedReviewerLogins,
            )
        }
        .navigationTitle("New Pull Request")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .cancellationAction) {
                Button("Cancel") { dismiss() }
            }
            ToolbarItem(placement: .confirmationAction) {
                Button("Create") {
                    Task { await createPullRequest() }
                }
                .disabled(!canCreate)
                .accessibilityIdentifier("pr-create-submit")
            }
        }
        .errorAlert(message: $errorMessage, isPresented: $showError)
        .task {
            await loadInitialData()
        }
    }

    private func loadInitialData() async {
        async let branchResult: () = loadBranches()
        async let metadataResult: () = loadMetadata()
        _ = await (branchResult, metadataResult)
    }

    private func loadBranches() async {
        guard let repositoryService else { return }
        isLoadingBranches = true
        do {
            branches = try await repositoryService.fetchBranches(owner: owner, repo: repo)
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }
        isLoadingBranches = false
    }

    private func loadMetadata() async {
        guard let repositoryService else { return }
        let metadata = await loadRepositoryMetadata(service: repositoryService, owner: owner, repo: repo)
        availableLabels = metadata.labels
        availableMilestones = metadata.milestones
        availableAssignees = metadata.assignees
    }

    private func createPullRequest() async {
        guard let prService else { return }
        isSubmitting = true
        do {
            // Reuse the already-created PR on retry so a failed reviewer request
            // doesn't open a duplicate PR when the user taps Create again.
            let prNumber: Int
            if let existingNumber = createdPRNumber {
                prNumber = existingNumber
            } else {
                let trimmedBody = bodyText.trimmingCharacters(in: .whitespacesAndNewlines)
                let createdPR = try await prService.createPullRequest(
                    owner: owner,
                    repo: repo,
                    title: title.trimmingCharacters(in: .whitespacesAndNewlines),
                    head: headBranch,
                    base: baseBranch,
                    body: trimmedBody.isEmpty ? nil : trimmedBody,
                    labels: selectedLabelIDs.isEmpty ? nil : Array(selectedLabelIDs),
                    milestone: selectedMilestoneID,
                    assignees: selectedAssigneeLogins.isEmpty ? nil : Array(selectedAssigneeLogins),
                )
                createdPRNumber = createdPR.number
                prNumber = createdPR.number
            }
            if !selectedReviewerLogins.isEmpty {
                do {
                    try await prService.requestReviewers(
                        owner: owner,
                        repo: repo,
                        index: prNumber,
                        reviewers: Array(selectedReviewerLogins),
                    )
                } catch {
                    errorMessage = "PR created, but requesting reviewers failed: \(error.localizedDescription)"
                    showError = true
                    NotificationCenter.default.post(name: .pullRequestsDidChange, object: nil)
                    onCreated()
                    isSubmitting = false
                    return
                }
            }
            NotificationCenter.default.post(name: .pullRequestsDidChange, object: nil)
            onCreated()
            dismiss()
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }
        isSubmitting = false
    }

    #if DEBUG
        init(preview _: Void, repository: Repository, authService: AuthenticationService,
             branches: [Branch] = [], baseBranch: String = "main",
             availableLabels: [IssueLabel] = [], availableMilestones: [IssueMilestone] = [],
             availableAssignees: [User] = [])
        {
            self.repository = repository
            self.authService = authService
            prService = nil
            repositoryService = nil
            onCreated = {}
            embeddedInNavigation = false
            _branches = State(initialValue: branches)
            _baseBranch = State(initialValue: baseBranch)
            _isLoadingBranches = State(initialValue: false)
            _availableLabels = State(initialValue: availableLabels)
            _availableMilestones = State(initialValue: availableMilestones)
            _availableAssignees = State(initialValue: availableAssignees)
        }
    #endif
}

#if DEBUG
    #Preview {
        NavigationStack {
            PullRequestCreateView(
                preview: (),
                repository: .preview,
                authService: .previewDefault,
                branches: Branch.previewList,
                baseBranch: "main",
                availableLabels: IssueLabel.previewList,
                availableMilestones: [.preview],
                availableAssignees: [.preview, .previewBot],
            )
        }
    }
#endif
