import ForgejoKit
import HighlightSwift
import SwiftUI

struct FileViewerView: View {
    let repository: Repository
    let filePath: String
    let fileName: String
    let ref: String?
    @State private var authService: AuthenticationService
    @State private var fileContent: String?
    @State private var fileSha: String?
    @State private var isLoading = false
    @State private var errorMessage: String?
    @State private var showError = false
    @State private var isEditing = false
    @State private var editedContent: String = ""
    @State private var showCommitSheet = false
    @State private var navigateToFilePath: String?

    private let repositoryService: RepositoryService?

    init(
        repository: Repository, filePath: String, fileName: String,
        authService: AuthenticationService, ref: String? = nil,
    ) {
        self.repository = repository
        self.filePath = filePath
        self.fileName = fileName
        self.authService = authService
        self.ref = ref
        repositoryService = authService.client.map { RepositoryService(client: $0) }
    }

    var body: some View {
        Group {
            if isLoading {
                VStack {
                    ProgressView("Loading file...")
                        .padding()
                }
            } else if let content = fileContent {
                if isEditing {
                    TextEditor(text: $editedContent)
                        .font(.system(.body, design: .monospaced))
                        .autocorrectionDisabled()
                        .textInputAutocapitalization(.never)
                } else {
                    if isMarkdownFile {
                        ScrollView {
                            MarkdownPreview(
                                text: content,
                                baseURL: markdownBaseURL,
                                onNavigateToFile: { path in
                                    navigateToFilePath = path
                                },
                            )
                            .padding()
                        }
                    } else {
                        let lines = content.components(separatedBy: "\n")
                        let lineNumberText = (1 ... lines.count).map(String.init).joined(separator: "\n")

                        ScrollView(.vertical) {
                            HStack(alignment: .top, spacing: 0) {
                                Text(lineNumberText)
                                    .font(.system(.body, design: .monospaced))
                                    .foregroundStyle(.quaternary)
                                    .multilineTextAlignment(.trailing)
                                    .padding(.leading, 8)
                                    .padding(.trailing, 8)
                                    .padding(.vertical, 12)
                                    .glassEffect(.regular)

                                ScrollView(.horizontal, showsIndicators: false) {
                                    CodeText(content)
                                        .fixedSize(horizontal: true, vertical: false)
                                        .textSelection(.enabled)
                                        .padding(12)
                                }
                            }
                        }
                    }
                }
            } else {
                ContentUnavailableView {
                    Label("Could not load file", systemImage: "doc.text.fill.badge.questionmark")
                        .foregroundStyle(.orange)
                } description: {
                    Text("The file could not be loaded.")
                }
            }
        }
        .navigationTitle(fileName)
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItemGroup(placement: .topBarTrailing) {
                if isEditing {
                    Button("Cancel") {
                        isEditing = false
                        editedContent = fileContent ?? ""
                    }
                    .accessibilityIdentifier("file-edit-cancel")
                    Button("Commit") {
                        showCommitSheet = true
                    }
                    .disabled(editedContent == fileContent)
                    .accessibilityIdentifier("file-edit-commit")
                } else if fileContent != nil, fileSha != nil {
                    Button {
                        editedContent = fileContent ?? ""
                        isEditing = true
                    } label: {
                        Image(systemName: "pencil")
                    }
                    .accessibilityLabel("Edit file")
                    .accessibilityIdentifier("file-edit-button")
                }
            }
        }
        .sheet(isPresented: $showCommitSheet) {
            if let repositoryService, let currentSha = fileSha {
                CommitSheetView(
                    fileName: fileName,
                    repositoryService: repositoryService,
                    repository: repository,
                    filePath: filePath,
                    editedContent: editedContent,
                    fileSha: currentSha,
                    branch: ref,
                    onCommit: { _ in
                        isEditing = false
                        await loadFile()
                    },
                )
            }
        }
        .task {
            await loadFile()
        }
        .errorAlert(message: $errorMessage, isPresented: $showError)
        .navigationDestination(item: $navigateToFilePath) { path in
            let fileName = path.components(separatedBy: "/").last ?? path
            FileViewerView(
                repository: repository,
                filePath: path,
                fileName: fileName,
                authService: authService,
                ref: ref,
            )
        }
    }

    private var isMarkdownFile: Bool {
        let lowercased = fileName.lowercased()
        return lowercased.hasSuffix(".md") || lowercased.hasSuffix(".markdown")
    }

    private var markdownBaseURL: URL? {
        guard let serverURL = authService.client?.serverURL else { return nil }
        let branch = ref ?? repository.defaultBranch ?? "main"
        let encodedBranch = branch.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? branch
        let directory = filePath.contains("/")
            ? filePath.components(separatedBy: "/").dropLast()
            .map { $0.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? $0 }
            .joined(separator: "/") + "/"
            : ""
        return URL(string: "\(serverURL)/\(repository.fullName)/src/branch/\(encodedBranch)/\(directory)")
    }

    #if DEBUG
        init(preview _: Void, repository: Repository, filePath: String, fileName: String,
             authService: AuthenticationService, ref: String? = nil,
             fileContent: String? = nil, fileSha: String? = nil)
        {
            self.repository = repository
            self.filePath = filePath
            self.fileName = fileName
            self.authService = authService
            self.ref = ref
            repositoryService = nil
            _fileContent = State(initialValue: fileContent)
            _fileSha = State(initialValue: fileSha)
        }
    #endif

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

        do {
            let fileContentData = try await repositoryService.fetchFileContent(
                owner: repository.owner,
                repo: repository.repoName,
                path: filePath,
                ref: ref,
            )

            fileContent = fileContentData.decodedContent
            fileSha = fileContentData.sha

        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }

        isLoading = false
    }
}

struct CommitSheetView: View {
    let fileName: String
    let repositoryService: RepositoryService
    let repository: Repository
    let filePath: String
    let editedContent: String
    let fileSha: String
    let branch: String?
    let onCommit: (FileContent) async -> Void

    @Environment(\.dismiss) private var dismiss
    @State private var commitMessage: String = ""
    @State private var isCommitting = false
    @State private var errorMessage: String?
    @State private var showError = false

    var body: some View {
        NavigationStack {
            Form {
                Section("Commit Message") {
                    TextField("Update \(fileName)", text: $commitMessage)
                        .accessibilityIdentifier("commit-message-field")
                }
            }
            .navigationTitle("Commit Changes")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") {
                        dismiss()
                    }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button("Commit") {
                        Task {
                            await commit()
                        }
                    }
                    .disabled(isCommitting)
                    .accessibilityIdentifier("commit-submit")
                }
            }
            .errorAlert(message: $errorMessage, isPresented: $showError)
        }
    }

    private func commit() async {
        isCommitting = true

        do {
            let message = commitMessage.isEmpty ? "Update \(fileName)" : commitMessage

            let updatedFile = try await repositoryService.updateFile(
                owner: repository.owner,
                repo: repository.repoName,
                path: filePath,
                content: editedContent,
                sha: fileSha,
                message: message,
                branch: branch,
            )

            await onCommit(updatedFile)
            dismiss()
            return
        } catch {
            errorMessage = error.localizedDescription
            showError = true
        }

        isCommitting = false
    }
}

#if DEBUG
    #Preview("Markdown") {
        NavigationStack {
            FileViewerView(
                preview: (),
                repository: .preview,
                filePath: "README.md",
                fileName: "README.md",
                authService: .previewDefault,
                fileContent: """
                # test-repo

                A sample repository for testing.

                ## Getting Started

                ```bash
                git clone https://example.com/testadmin/test-repo.git
                cd test-repo
                ```

                ## Features

                - Written in **Python**
                - Includes `hello.py` and `src/main.py`
                - Licensed under MIT
                """,
                fileSha: "abc123",
            )
        }
    }

    #Preview("Code") {
        NavigationStack {
            FileViewerView(
                preview: (),
                repository: .preview,
                filePath: "hello.py",
                fileName: "hello.py",
                authService: .previewDefault,
                fileContent: """
                #!/usr/bin/env python3

                def greet(name: str) -> str:
                    \"\"\"Return a greeting message.\"\"\"
                    return f"Hello, {name}!"

                if __name__ == "__main__":
                    print(greet("World"))
                """,
                fileSha: "def456",
            )
        }
    }
#endif
