import ForgejoKit
import SwiftUI

struct PendingInlineComment: Identifiable {
    let id = UUID()
    let body: String
    let path: String
    let line: DiffLine

    var reviewComment: CreateReviewComment? {
        guard let position = line.diffPosition else { return nil }
        return CreateReviewComment(
            body: body,
            path: path,
            oldPosition: line.type == .deletion ? position : nil,
            newPosition: line.type == .deletion ? nil : position,
        )
    }
}

struct PullRequestReviewSheet: View {
    let repository: Repository
    let prNumber: Int
    let isOwnPR: Bool
    @State private var authService: AuthenticationService

    @State private var reviewEvent: ReviewEvent = .comment
    @State private var reviewBody = ""
    @State private var isSubmitting = false
    @State private var errorMessage: String?
    @State private var showError = false
    @State private var parsedDiff: ParsedDiff?
    @State private var isLoadingDiff = false
    @State private var inlineCommentContext: InlineCommentContext?
    @State private var pendingComments: [PendingInlineComment] = []
    @Environment(\.dismiss) private var dismiss

    private let prService: PullRequestService?
    private let onSubmitted: () -> Void

    init(
        repository: Repository,
        prNumber: Int,
        authService: AuthenticationService,
        isOwnPR: Bool = false,
        onSubmitted: @escaping () -> Void,
    ) {
        self.repository = repository
        self.prNumber = prNumber
        self.isOwnPR = isOwnPR
        self.authService = authService
        prService = authService.client.map { PullRequestService(client: $0) }
        self.onSubmitted = onSubmitted
    }

    private var owner: String {
        repository.owner
    }

    private var repo: String {
        repository.repoName
    }

    var body: some View {
        NavigationStack {
            List {
                Section("Review Type") {
                    Picker("Type", selection: $reviewEvent) {
                        Label(ReviewEvent.comment.title, systemImage: ReviewEvent.comment.systemImage)
                            .tag(ReviewEvent.comment)
                        if !isOwnPR {
                            Label(ReviewEvent.approved.title, systemImage: ReviewEvent.approved.systemImage)
                                .tag(ReviewEvent.approved)
                            Label(ReviewEvent.requestChanges.title, systemImage: ReviewEvent.requestChanges.systemImage)
                                .tag(ReviewEvent.requestChanges)
                        }
                    }
                    .pickerStyle(.inline)
                    .labelsHidden()
                    .accessibilityIdentifier("review-type-picker")
                }

                Section("Changes") {
                    if isLoadingDiff {
                        HStack {
                            Spacer()
                            ProgressView()
                            Spacer()
                        }
                    } else if let diff = parsedDiff {
                        DiffView(diff: diff) { line, path in
                            inlineCommentContext = InlineCommentContext(line: line, path: path)
                        }
                        .listRowInsets(EdgeInsets())
                    }
                }

                if !pendingComments.isEmpty {
                    Section("Inline Comments (\(pendingComments.count))") {
                        ForEach(pendingComments) { comment in
                            VStack(alignment: .leading, spacing: 4) {
                                Text(comment.path)
                                    .font(.system(.caption, design: .monospaced))
                                    .foregroundStyle(.secondary)
                                Text(comment.body)
                                    .font(.subheadline)
                            }
                            .padding(.vertical, 2)
                        }
                        .onDelete { offsets in
                            pendingComments.remove(atOffsets: offsets)
                        }
                    }
                }

                DescriptionEditorSection(text: $reviewBody, title: "Review Body")
            }
            .listStyle(.insetGrouped)
            .navigationTitle("Submit Review")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") { dismiss() }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button("Submit") {
                        Task { await submitReview() }
                    }
                    .disabled(isSubmitting)
                    .accessibilityIdentifier("review-submit")
                }
            }
            .errorAlert(message: $errorMessage, isPresented: $showError)
            .sheet(item: $inlineCommentContext) { context in
                AddInlineCommentSheet(context: context) { comment in
                    pendingComments.append(comment)
                }
            }
            .task {
                await loadDiff()
            }
        }
    }

    private func loadDiff() async {
        guard let prService else { return }
        isLoadingDiff = true
        do {
            let raw = try await prService.fetchDiff(owner: owner, repo: repo, index: prNumber)
            parsedDiff = DiffParser.parse(raw)
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }
        isLoadingDiff = false
    }

    private func submitReview() async {
        guard let prService else { return }
        isSubmitting = true
        do {
            _ = try await prService.createReview(
                owner: owner,
                repo: repo,
                index: prNumber,
                body: reviewBody,
                event: reviewEvent.rawValue,
                comments: pendingComments.compactMap(\.reviewComment),
            )
            onSubmitted()
            dismiss()
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }
        isSubmitting = false
    }

    #if DEBUG
        init(preview _: Void, repository: Repository, prNumber: Int, authService: AuthenticationService,
             isOwnPR: Bool = false, parsedDiff: ParsedDiff? = nil)
        {
            self.repository = repository
            self.prNumber = prNumber
            self.isOwnPR = isOwnPR
            self.authService = authService
            prService = nil
            onSubmitted = {}
            _parsedDiff = State(initialValue: parsedDiff)
            _isLoadingDiff = State(initialValue: false)
        }
    #endif
}

private struct AddInlineCommentSheet: View {
    let context: InlineCommentContext
    let onAdd: (PendingInlineComment) -> Void

    @State private var commentBody = ""
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        NavigationStack {
            VStack(spacing: 0) {
                DiffLineHeader(path: context.path, line: context.line)

                TextEditor(text: $commentBody)
                    .frame(maxHeight: .infinity)
                    .scrollContentBackground(.hidden)
                    .padding(8)
                    .background(Color(.tertiarySystemFill))
                    .clipShape(RoundedRectangle(cornerRadius: 8))
                    .padding(.horizontal)
                    .padding(.bottom)
            }
            .navigationTitle("Add Comment")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") { dismiss() }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button("Add") {
                        onAdd(PendingInlineComment(
                            body: commentBody,
                            path: context.path,
                            line: context.line,
                        ))
                        dismiss()
                    }
                    .disabled(commentBody.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
                }
            }
        }
    }
}

#if DEBUG
    #Preview {
        PullRequestReviewSheet(
            preview: (),
            repository: .preview,
            prNumber: 4,
            authService: .previewDefault,
            parsedDiff: .preview,
        )
    }
#endif
