import ForgejoKit

// swiftlint:disable file_length
import SwiftUI

struct FileNavigation: Hashable {
    let path: String
    let branch: String
}

struct RepositoryDetailView: View {
    let repository: Repository
    @State private var authService: AuthenticationService
    @State private var selectedTab: DetailTab = .code
    @State private var fileNavigation: FileNavigation?
    @State private var selectedBranch: String
    @State private var showCommitHistory = false
    @State private var branches: [Branch] = []
    @State private var showBranchPicker = false

    private let repositoryService: RepositoryService?

    init(repository: Repository, authService: AuthenticationService) {
        self.repository = repository
        self.authService = authService
        repositoryService = authService.client.map { RepositoryService(client: $0) }
        _selectedBranch = State(initialValue: repository.defaultBranch ?? "main")
    }

    enum DetailTab: String {
        case code = "Code"
        case issues = "Issues"
        case pulls = "PRs"
        case actions = "Actions"

        var icon: String {
            switch self {
            case .code: "doc.text"
            case .issues: "exclamationmark.circle"
            case .pulls: "arrow.triangle.pull"
            case .actions: "play.circle"
            }
        }
    }

    private var availableTabs: [DetailTab] {
        var tabs: [DetailTab] = [.code]
        if repository.hasIssues != false {
            tabs.append(.issues)
        }
        if repository.hasPullRequests != false {
            tabs.append(.pulls)
        }
        if repository.hasActions == true {
            tabs.append(.actions)
        }
        return tabs
    }

    var body: some View {
        VStack(spacing: 0) {
            // Repository header
            VStack(alignment: .leading, spacing: 12) {
                HStack {
                    VStack(alignment: .leading, spacing: 4) {
                        Text(repository.name)
                            .font(.title2)
                            .fontWeight(.bold)

                        if let description = repository.description {
                            Text(description)
                                .font(.subheadline)
                                .foregroundStyle(.secondary)
                        }
                    }

                    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")
                    }
                }

                // Stats
                HStack(spacing: 20) {
                    Label("\(repository.starsCount ?? 0)", systemImage: "star.fill")
                        .font(.subheadline)

                    Label("\(repository.forksCount ?? 0)", systemImage: "tuningfork")
                        .font(.subheadline)

                    if let language = repository.language {
                        HStack(spacing: 4) {
                            Circle()
                                .fill(colorForLanguage(language))
                                .frame(width: 12, height: 12)
                            Text(language)
                                .font(.subheadline)
                        }
                    }
                }
                .foregroundStyle(.secondary)
            }
            .padding()

            Divider()

            // Tab selector
            Picker("View", selection: $selectedTab) {
                ForEach(availableTabs, id: \.self) { tab in
                    Label(tab.rawValue, systemImage: tab.icon)
                        .tag(tab)
                }
            }
            .pickerStyle(.segmented)
            .padding()
            .accessibilityIdentifier("repo-detail-tab-picker")

            // Tab content
            TabView(selection: $selectedTab) {
                RepositoryCodeView(
                    repository: repository,
                    authService: authService,
                    selectedBranch: $selectedBranch,
                    onFileNavigation: { path, branch in
                        fileNavigation = FileNavigation(path: path, branch: branch)
                    },
                )
                .tag(DetailTab.code)

                IssueListView(
                    repository: repository,
                    authService: authService,
                )
                .tag(DetailTab.issues)

                PullRequestListView(
                    repository: repository,
                    authService: authService,
                )
                .tag(DetailTab.pulls)

                ActionsTabView(
                    repository: repository,
                    authService: authService,
                )
                .tag(DetailTab.actions)
            }
            .tabViewStyle(.page(indexDisplayMode: .never))
        }
        .navigationTitle(repository.name)
        .navigationBarTitleDisplayMode(.inline)
        .navigationDestination(item: $fileNavigation) { nav in
            let fileName = nav.path.components(separatedBy: "/").last ?? nav.path
            FileViewerView(
                repository: repository,
                filePath: nav.path,
                fileName: fileName,
                authService: authService,
                ref: nav.branch,
            )
        }
        .navigationDestination(isPresented: $showCommitHistory) {
            CommitHistoryView(
                repository: repository,
                branch: $selectedBranch,
                authService: authService,
            )
            .navigationTitle("Commits")
            .navigationBarTitleDisplayMode(.inline)
        }
        .task {
            guard let repositoryService else { return }
            do {
                branches = try await repositoryService.fetchBranches(
                    owner: repository.owner,
                    repo: repository.repoName,
                )
            } catch {
                // Non-critical, branch selector stays disabled
            }
        }
        .toolbar {
            if selectedTab == .code {
                ToolbarItem(placement: .topBarTrailing) {
                    Button {
                        showBranchPicker = true
                    } label: {
                        HStack(spacing: 4) {
                            Image(systemName: "arrow.triangle.branch")
                            Text(selectedBranch)
                                .lineLimit(1)
                                .frame(maxWidth: 140)
                        }
                        .font(.subheadline.weight(.medium))
                    }
                    .disabled(branches.isEmpty)
                    .accessibilityIdentifier("branch-selector")
                }
                ToolbarItem(placement: .topBarTrailing) {
                    Button {
                        showCommitHistory = true
                    } label: {
                        Image(systemName: "clock.arrow.circlepath")
                    }
                    .accessibilityLabel("Commit history")
                    .accessibilityIdentifier("commits-button")
                }
            }
        }
        .sheet(isPresented: $showBranchPicker) {
            NavigationStack {
                List(branches) { branch in
                    Button {
                        selectedBranch = branch.name
                        showBranchPicker = false
                    } label: {
                        HStack {
                            Text(branch.name)
                            Spacer()
                            if branch.name == selectedBranch {
                                Image(systemName: "checkmark")
                                    .foregroundStyle(.blue)
                            }
                        }
                    }
                    .accessibilityIdentifier("branch-option-\(branch.name)")
                }
                .navigationTitle("Switch Branch")
                .navigationBarTitleDisplayMode(.inline)
                .toolbar {
                    ToolbarItem(placement: .cancellationAction) {
                        Button("Cancel") {
                            showBranchPicker = false
                        }
                    }
                }
            }
            .presentationDetents([.medium])
        }
    }
}

// swiftlint:disable:next type_body_length
struct RepositoryCodeView: View {
    let repository: Repository
    @State private var authService: AuthenticationService
    @State private var contents: [RepositoryContent] = []
    @State private var currentPath: String = ""
    @State private var pathComponents: [String] = []
    @State private var isLoading = false
    @State private var errorMessage: String?
    @State private var showError = false
    @State private var readmeContent: String?
    @State private var scrollToFiles = false
    @State private var scrollToReadme = false
    @State private var isScrolledToFiles = false
    @Binding var selectedBranch: String
    @State private var branchContentTask: Task<Void, Never>?
    @State private var contentLoadTask: Task<Void, Never>?
    @State private var hasLoaded = false

    private let repositoryService: RepositoryService?
    var onFileNavigation: ((String, String) -> Void)?

    init(
        repository: Repository,
        authService: AuthenticationService,
        selectedBranch: Binding<String>,
        onFileNavigation: ((String, String) -> Void)? = nil,
    ) {
        self.repository = repository
        self.authService = authService
        repositoryService = authService.client.map { RepositoryService(client: $0) }
        self.onFileNavigation = onFileNavigation
        _selectedBranch = selectedBranch
    }

    private var readmeBaseURL: URL? {
        guard let serverURL = authService.client?.serverURL else { return nil }
        let encodedBranch = selectedBranch.addingPercentEncoding(
            withAllowedCharacters: .urlPathAllowed,
        ) ?? selectedBranch
        let pathSuffix: String = if currentPath.isEmpty {
            ""
        } else {
            currentPath.components(separatedBy: "/")
                .map { $0.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? $0 }
                .joined(separator: "/") + "/"
        }
        return URL(string: "\(serverURL)/\(repository.fullName)/src/branch/\(encodedBranch)/\(pathSuffix)")
    }

    var body: some View {
        ZStack(alignment: .bottomTrailing) {
            VStack(spacing: 0) {
                // Breadcrumb navigation
                if !pathComponents.isEmpty {
                    ScrollView(.horizontal, showsIndicators: false) {
                        HStack(spacing: 4) {
                            Button {
                                navigateToRoot()
                            } label: {
                                Text(repository.name)
                                    .font(.subheadline)
                            }

                            ForEach(Array(pathComponents.enumerated()), id: \.offset) { index, component in
                                Text("/")
                                    .foregroundStyle(.secondary)
                                    .font(.subheadline)

                                Button {
                                    navigateToPath(upTo: index)
                                } label: {
                                    Text(component)
                                        .font(.subheadline)
                                }
                            }
                        }
                        .padding(.horizontal)
                        .padding(.vertical, 8)
                    }
                }

                // Content list
                ScrollViewReader { proxy in
                    List {
                        if let readme = readmeContent {
                            Section("README") {
                                MarkdownPreview(
                                    text: readme,
                                    baseURL: readmeBaseURL,
                                    onNavigateToFile: { path in
                                        onFileNavigation?(path, selectedBranch)
                                    },
                                )
                                .padding()
                            }
                            .id("readme")
                        }

                        // Files and folders
                        Section("Files") {
                            if isLoading {
                                HStack {
                                    Spacer()
                                    ProgressView()
                                    Spacer()
                                }
                            } else {
                                ForEach(contents) { content in
                                    if content.type == .dir {
                                        Button {
                                            handleContentTap(content)
                                        } label: {
                                            ContentRow(content: content)
                                        }
                                        .buttonStyle(.plain)
                                        .accessibilityIdentifier("directory-\(content.name)")
                                    } else {
                                        NavigationLink {
                                            FileViewerView(
                                                repository: repository,
                                                filePath: currentPath.isEmpty
                                                    ? content.name : "\(currentPath)/\(content.name)",
                                                fileName: content.name,
                                                authService: authService,
                                                ref: selectedBranch,
                                            )
                                        } label: {
                                            ContentRow(content: content)
                                        }
                                    }
                                }
                            }
                        }
                        .id("files")
                    }
                    .onChange(of: scrollToFiles) {
                        if scrollToFiles {
                            withAnimation {
                                proxy.scrollTo("files", anchor: .top)
                            }
                            scrollToFiles = false
                            isScrolledToFiles = true
                        }
                    }
                    .onChange(of: scrollToReadme) {
                        if scrollToReadme {
                            withAnimation {
                                proxy.scrollTo("readme", anchor: .top)
                            }
                            scrollToReadme = false
                            isScrolledToFiles = false
                        }
                    }
                }
            }
            .task {
                if !hasLoaded {
                    await loadContents()
                }
            }
            .onChange(of: selectedBranch) {
                pathComponents = []
                currentPath = ""
                readmeContent = nil
                branchContentTask?.cancel()
                branchContentTask = Task {
                    await loadContents()
                }
            }
            .errorAlert(message: $errorMessage, isPresented: $showError)

            // Floating button to jump between README and Files
            if readmeContent != nil {
                Button {
                    if isScrolledToFiles {
                        scrollToReadme = true
                    } else {
                        scrollToFiles = true
                    }
                } label: {
                    Label(
                        isScrolledToFiles ? "README" : "Files",
                        systemImage: isScrolledToFiles ? "arrow.up.doc.fill" : "arrow.down.doc.fill",
                    )
                    .font(.headline)
                    .foregroundStyle(.white)
                    .padding(.horizontal, 16)
                    .padding(.vertical, 12)
                }
                .glassEffect(.regular.tint(.blue).interactive())
                .padding(.trailing, 20)
                .padding(.bottom, 20)
                .animation(.easeInOut, value: isScrolledToFiles)
                .accessibilityIdentifier("code-scroll-toggle")
            }
        }
    }

    private func loadContents() async {
        guard let repositoryService else { return }
        isLoading = true
        errorMessage = nil

        do {
            contents = try await repositoryService.fetchContents(
                owner: repository.owner,
                repo: repository.repoName,
                path: currentPath,
                ref: selectedBranch,
            )

            // Sort: directories first, then files
            contents.sort { lhs, rhs in
                if lhs.type == .dir, rhs.type != .dir {
                    true
                } else if lhs.type != .dir, rhs.type == .dir {
                    false
                } else {
                    lhs.name.localizedCaseInsensitiveCompare(rhs.name) == .orderedAscending
                }
            }

            readmeContent = nil
            await loadReadme(owner: repository.owner, repo: repository.repoName)
            hasLoaded = true

        } catch is CancellationError {
            // Ignore cancellation
            return
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }

        isLoading = false
    }

    private func loadReadme(owner: String, repo: String) async {
        guard let repositoryService else { return }
        let readmeFile = contents.first {
            $0.type == .file && $0.name.lowercased().hasPrefix("readme")
        }
        guard let readmeFile else { return }

        do {
            let readmePath = currentPath.isEmpty ? readmeFile.name : "\(currentPath)/\(readmeFile.name)"
            let fileContent = try await repositoryService.fetchFileContent(
                owner: owner,
                repo: repo,
                path: readmePath,
                ref: selectedBranch,
            )
            readmeContent = fileContent.decodedContent
        } catch {
            // README exists in listing but couldn't be fetched, ignore
        }
    }

    private func handleContentTap(_ content: RepositoryContent) {
        if content.type == .dir {
            pathComponents.append(content.name)
            currentPath = pathComponents.joined(separator: "/")
            contentLoadTask?.cancel()
            contentLoadTask = Task {
                await loadContents()
            }
        }
    }

    private func navigateToRoot() {
        pathComponents = []
        currentPath = ""
        contentLoadTask?.cancel()
        contentLoadTask = Task {
            await loadContents()
        }
    }

    private func navigateToPath(upTo index: Int) {
        pathComponents = Array(pathComponents.prefix(index + 1))
        currentPath = pathComponents.joined(separator: "/")
        contentLoadTask?.cancel()
        contentLoadTask = Task {
            await loadContents()
        }
    }
}

struct ContentRow: View {
    let content: RepositoryContent

    var body: some View {
        HStack(spacing: 12) {
            Image(systemName: iconForType(content.type))
                .foregroundStyle(content.type == .dir ? .blue : .secondary)
                .frame(width: 24)

            VStack(alignment: .leading, spacing: 2) {
                Text(content.name)
                    .foregroundStyle(.primary)

                if content.type == .file {
                    Text(formatFileSize(content.size))
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }

            Spacer()

            if content.type == .dir {
                Image(systemName: "chevron.right")
                    .foregroundStyle(.tertiary)
                    .font(.caption)
            }
        }
        .padding(.vertical, 4)
    }

    private func iconForType(_ type: RepositoryContent.ContentType) -> String {
        switch type {
        case .dir:
            "folder.fill"
        case .file:
            "doc.text"
        case .symlink:
            "link"
        case .submodule:
            "folder.badge.gearshape"
        }
    }

    private func formatFileSize(_ bytes: Int) -> String {
        let formatter = ByteCountFormatter()
        formatter.countStyle = .file
        return formatter.string(fromByteCount: Int64(bytes))
    }
}

#if DEBUG
    #Preview {
        NavigationStack {
            RepositoryDetailView(repository: .preview, authService: .previewDefault)
        }
    }
#endif
