import ForgejoKit
import SwiftUI

struct PullRequestListView: View {
    let repository: Repository
    @State private var authService: AuthenticationService
    @State private var pagination = PaginationState<PullRequest>()
    @State private var stateFilter: PullRequestState = .open
    @State private var showCreateSheet = false

    private let prService: PullRequestService?

    init(repository: Repository, authService: AuthenticationService) {
        self.repository = repository
        self.authService = authService
        prService = authService.client.map { PullRequestService(client: $0) }
    }

    private var owner: String {
        repository.owner
    }

    private var repo: String {
        repository.repoName
    }

    var body: some View {
        @Bindable var pagination = pagination
        ZStack(alignment: .bottomTrailing) {
            VStack(spacing: 0) {
                SegmentedPickerSection(
                    title: "State",
                    selection: $stateFilter,
                    options: [("Open", .open), ("Closed", .closed)],
                    accessibilityIdentifier: "pr-state-picker",
                )

                List {
                    if pagination.isLoading, pagination.items.isEmpty {
                        LoadingListSection()
                    } else if pagination.notFound {
                        ContentUnavailableView {
                            Label("Pull Requests Unavailable", systemImage: "exclamationmark.circle")
                        } description: {
                            Text("Pull requests are not available for this repository.")
                        }
                    } else if pagination.items.isEmpty {
                        ContentUnavailableView {
                            Label(
                                "No Pull Requests",
                                systemImage: stateFilter == .open
                                    ? "checkmark.circle" : "arrow.triangle.pull",
                            )
                            .foregroundStyle(stateFilter == .open ? .green : .secondary)
                        } description: {
                            Text(
                                stateFilter == .open
                                    ? "All clear, no open pull requests."
                                    : "No closed pull requests found.",
                            )
                        }
                    } else {
                        Section {
                            ForEach(pagination.items) { pullRequest in
                                NavigationLink {
                                    PullRequestDetailView(
                                        repository: repository,
                                        prNumber: pullRequest.number,
                                        authService: authService,
                                    )
                                } label: {
                                    PullRequestRow(pullRequest: pullRequest)
                                }
                            }

                            if pagination.hasMore {
                                ProgressView()
                                    .frame(maxWidth: .infinity)
                                    .listRowBackground(Color.clear)
                                    .accessibilityIdentifier("load-more-indicator")
                                    .task {
                                        await loadMorePullRequests()
                                    }
                            }
                        }
                    }
                }
                .listStyle(.insetGrouped)
                .refreshable {
                    await reloadPullRequests().value
                }
            }

            // Floating glass create button
            FloatingCreateButton(action: { showCreateSheet = true })
                .accessibilityIdentifier("pr-create-button")
        }
        .sheet(isPresented: $showCreateSheet) {
            PullRequestCreateView(
                repository: repository,
                authService: authService,
            ) {
                reloadPullRequests()
            }
        }
        .task {
            if !pagination.hasLoaded {
                reloadPullRequests()
            }
        }
        .onReceive(NotificationCenter.default.publisher(for: .pullRequestsDidChange)) { _ in
            pagination.invalidate()
            reloadPullRequests()
        }
        .onChange(of: stateFilter) {
            reloadPullRequests(clearItems: true)
        }
        .errorAlert(message: $pagination.errorMessage, isPresented: $pagination.showError)
    }

    @discardableResult
    private func reloadPullRequests(clearItems: Bool = false) -> Task<Void, Never> {
        guard let prService else { return Task {} }
        return pagination.reload(clearItems: clearItems) { [self] page, limit in
            do {
                return try await prService.fetchPullRequests(
                    owner: owner,
                    repo: repo,
                    state: stateFilter.rawValue,
                    page: page,
                    limit: limit,
                )
            } catch let error as ServiceError where error.httpStatusCode == 404 {
                pagination.notFound = true
                return []
            }
        }
    }

    private func loadMorePullRequests() async {
        guard let prService else { return }
        await pagination.loadMore { page, limit in
            try await prService.fetchPullRequests(
                owner: owner,
                repo: repo,
                state: stateFilter.rawValue,
                page: page,
                limit: limit,
            )
        }
    }

    #if DEBUG
        init(preview _: Void, repository: Repository, authService: AuthenticationService, pullRequests: [PullRequest],
             notFound: Bool = false)
        {
            self.repository = repository
            self.authService = authService
            prService = nil
            _pagination = State(initialValue: PaginationState(items: pullRequests, notFound: notFound))
        }
    #endif
}

#if DEBUG
    #Preview {
        NavigationStack {
            PullRequestListView(
                preview: (),
                repository: .preview,
                authService: .previewDefault,
                pullRequests: [.preview],
            )
        }
    }

    #Preview("Pull Requests Unavailable") {
        NavigationStack {
            PullRequestListView(
                preview: (),
                repository: .preview,
                authService: .previewDefault,
                pullRequests: [],
                notFound: true,
            )
        }
    }
#endif

struct PullRequestRow: View {
    let pullRequest: PullRequest

    private var status: PRStatusStyle {
        PRStatusStyle(state: pullRequest.stateValue, merged: pullRequest.merged, draft: pullRequest.draft)
    }

    var body: some View {
        HStack(alignment: .top, spacing: 10) {
            Image(systemName: status.icon)
                .foregroundStyle(status.color)
                .font(.body)
                .padding(.top, 2)

            VStack(alignment: .leading, spacing: 4) {
                Text(pullRequest.title)
                    .font(.body)
                    .lineLimit(2)

                HStack(spacing: 6) {
                    Text("#\(pullRequest.number)")
                    Text("\(pullRequest.head.ref) \u{2192} \(pullRequest.base.ref)")
                        .lineLimit(1)
                    if pullRequest.comments > 0 {
                        Label("\(pullRequest.comments)", systemImage: "bubble.right")
                    }
                }
                .font(.caption)
                .foregroundStyle(.secondary)

                FlowLayout(spacing: 4) {
                    if pullRequest.draft == true {
                        Text("Draft")
                            .font(.caption2)
                            .fontWeight(.medium)
                            .padding(.horizontal, 8)
                            .padding(.vertical, 3)
                            .glassEffect(.regular)
                    }

                    ForEach(pullRequest.labels) { label in
                        IssueLabelView(label: label)
                    }
                }
            }
        }
        .padding(.vertical, 2)
        .stateAccent(status.color)
    }
}
