import ForgejoKit
import SwiftUI

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

    private let issueService: IssueService?

    init(repository: Repository, authService: AuthenticationService) {
        self.repository = repository
        self.authService = authService
        issueService = authService.client.map { IssueService(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: "issue-state-picker",
                )

                List {
                    if pagination.isLoading, pagination.items.isEmpty {
                        LoadingListSection()
                    } else if pagination.notFound {
                        ContentUnavailableView {
                            Label("Issues Unavailable", systemImage: "exclamationmark.circle")
                        } description: {
                            Text("Issues are not available for this repository.")
                        }
                    } else if pagination.items.isEmpty {
                        ContentUnavailableView {
                            Label(
                                "No Issues",
                                systemImage: stateFilter == .open
                                    ? "checkmark.circle" : "exclamationmark.circle",
                            )
                            .foregroundStyle(stateFilter == .open ? .green : .secondary)
                        } description: {
                            Text(
                                stateFilter == .open
                                    ? "All clear, no open issues to review."
                                    : "No closed issues found.",
                            )
                        }
                    } else {
                        Section {
                            ForEach(pagination.items) { issue in
                                NavigationLink {
                                    IssueDetailView(
                                        repository: repository,
                                        issueNumber: issue.number,
                                        authService: authService,
                                    )
                                } label: {
                                    IssueRow(issue: issue)
                                }
                            }

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

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

    @discardableResult
    private func reloadIssues(clearItems: Bool = false) -> Task<Void, Never> {
        guard let issueService else { return Task {} }
        return pagination.reload(clearItems: clearItems) { [self] page, limit in
            do {
                return try await issueService.fetchIssues(
                    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 loadMoreIssues() async {
        guard let issueService else { return }
        await pagination.loadMore { page, limit in
            try await issueService.fetchIssues(
                owner: owner,
                repo: repo,
                state: stateFilter.rawValue,
                page: page,
                limit: limit,
            )
        }
    }

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

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

    #Preview("Issues Unavailable") {
        NavigationStack {
            IssueListView(
                preview: (),
                repository: .preview,
                authService: .previewDefault,
                issues: [],
                notFound: true,
            )
        }
    }
#endif

struct IssueRow: View {
    let issue: Issue
    var instanceName: String?

    var body: some View {
        HStack(alignment: .top, spacing: 10) {
            Image(systemName: issue.stateValue == .open ? "circle.circle" : "checkmark.circle.fill")
                .foregroundStyle(issue.stateValue == .open ? .green : .purple)
                .font(.body)
                .padding(.top, 2)

            VStack(alignment: .leading, spacing: 4) {
                HStack(spacing: 6) {
                    Text(issue.title)
                        .font(.headline)
                        .lineLimit(2)
                    if let instanceName {
                        InstanceBadge(name: instanceName)
                    }
                }

                if let repository = issue.repository {
                    Text(repository.fullName)
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }

                HStack(spacing: 6) {
                    Text("#\(issue.number)")
                    if issue.comments > 0 {
                        Label("\(issue.comments)", systemImage: "bubble.right")
                    }
                }
                .font(.caption)
                .foregroundStyle(.secondary)

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