import ForgejoKit
import SwiftUI

struct RepositoryPickerView<Destination: View>: View {
    @State private var authService: AuthenticationService
    @State private var repositories: [Repository] = []
    @State private var isLoading = false
    @State private var errorMessage: String?
    @State private var showError = false
    @State private var searchText = ""
    @State private var searchTask: Task<Void, Never>?
    @State private var selectedRepository: Repository?
    @State private var hasMore = true
    @State private var currentPage = 1
    @Environment(\.dismiss) private var dismiss

    private let repositoryService: RepositoryService?
    private let pageSize = 20
    let destination: (Repository) -> Destination

    init(authService: AuthenticationService, @ViewBuilder destination: @escaping (Repository) -> Destination) {
        self.authService = authService
        repositoryService = authService.client.map { RepositoryService(client: $0) }
        self.destination = destination
    }

    var body: some View {
        NavigationStack {
            List {
                if isLoading, repositories.isEmpty {
                    LoadingListSection()
                } else if repositories.isEmpty {
                    ContentUnavailableView {
                        Label("No Repositories", systemImage: "folder.badge.questionmark")
                            .foregroundStyle(.secondary)
                    } description: {
                        Text(
                            searchText.isEmpty
                                ? "No repositories yet."
                                : "No repositories matching your search.",
                        )
                    }
                } else {
                    Section {
                        ForEach(repositories) { repo in
                            Button {
                                selectedRepository = repo
                            } label: {
                                RepositoryPickerRow(repository: repo)
                            }
                            .accessibilityIdentifier("repo-picker-\(repo.fullName)")
                        }

                        if hasMore {
                            ProgressView()
                                .frame(maxWidth: .infinity)
                                .listRowBackground(Color.clear)
                                .accessibilityIdentifier("load-more-indicator")
                                .task {
                                    await loadMoreRepositories()
                                }
                        }
                    }
                }
            }
            .listStyle(.insetGrouped)
            .navigationTitle("Select Repository")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") { dismiss() }
                }
            }
            .searchable(text: $searchText, prompt: "Search repositories")
            .task {
                await loadRepositories()
            }
            .debouncedSearch(text: $searchText, task: $searchTask) {
                currentPage = 1
                hasMore = true
                await loadRepositories()
            }
            .errorAlert(message: $errorMessage, isPresented: $showError)
            .navigationDestination(item: $selectedRepository) { repo in
                destination(repo)
            }
        }
    }

    private func loadRepositories() async {
        guard let repositoryService else { return }
        isLoading = true
        defer { isLoading = false }
        do {
            let fetched: [Repository] = if searchText.isEmpty {
                try await repositoryService.fetchUserRepositories(page: 1, limit: pageSize)
            } else {
                try await repositoryService.searchRepositories(query: searchText, page: 1, limit: pageSize)
            }
            repositories = fetched
            hasMore = fetched.count >= pageSize
            currentPage = 2
        } catch is CancellationError {
            // Ignore cancellation
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }
    }

    private func loadMoreRepositories() async {
        guard let repositoryService, hasMore, !isLoading else { return }
        isLoading = true
        defer { isLoading = false }
        do {
            let fetched: [Repository] = if searchText.isEmpty {
                try await repositoryService.fetchUserRepositories(page: currentPage, limit: pageSize)
            } else {
                try await repositoryService.searchRepositories(
                    query: searchText, page: currentPage, limit: pageSize,
                )
            }
            repositories.append(contentsOf: fetched)
            hasMore = fetched.count >= pageSize
            currentPage += 1
        } catch is CancellationError {
            // Ignore cancellation
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }
    }

    #if DEBUG
        init(preview _: Void, authService: AuthenticationService, repositories: [Repository],
             @ViewBuilder destination: @escaping (Repository) -> Destination)
        {
            self.authService = authService
            repositoryService = nil
            self.destination = destination
            _repositories = State(initialValue: repositories)
            _hasMore = State(initialValue: false)
        }
    #endif
}

private struct RepositoryPickerRow: View {
    let repository: Repository

    var body: some View {
        HStack {
            VStack(alignment: .leading, spacing: 4) {
                Text(repository.fullName)
                    .font(.body)

                if let description = repository.description, !description.isEmpty {
                    Text(description)
                        .font(.caption)
                        .foregroundStyle(.secondary)
                        .lineLimit(2)
                }
            }

            Spacer()

            if repository.private ?? false {
                RepoBadge(title: "Private", systemImage: "lock.fill")
            }

            if repository.archived ?? false {
                RepoBadge(title: "Archived", systemImage: "archivebox.fill")
            }

            if repository.mirror ?? false {
                RepoBadge(title: "Mirror", systemImage: "arrow.triangle.2.circlepath")
            }
        }
        .padding(.vertical, 2)
    }
}

#if DEBUG
    #Preview {
        RepositoryPickerView(preview: (), authService: .previewDefault, repositories: [.preview]) { repo in
            Text(repo.fullName)
        }
    }
#endif
