import ForgejoKit
import PhotosUI
import SwiftUI
import Textual
import UniformTypeIdentifiers

enum EditPreviewTab: String, CaseIterable {
    case edit = "Edit"
    case preview = "Preview"
}

struct MarkdownEditorField: View {
    @Binding var text: String
    @Binding var selectedTab: EditPreviewTab
    var minHeight: CGFloat = 150
    var showToolbar: Bool = false
    /// Called with (data, fileName, mimeType) when the user picks an image.
    /// Return the markdown snippet to insert, e.g. `![name](url)`.
    var onUploadImage: ((Data, String, String) async throws -> String)?

    var body: some View {
        VStack(spacing: 0) {
            Picker("Mode", selection: $selectedTab) {
                ForEach(EditPreviewTab.allCases, id: \.self) { tab in
                    Text(tab.rawValue).tag(tab)
                }
            }
            .pickerStyle(.segmented)
            .padding(.bottom, 10)

            if selectedTab == .edit {
                VStack(spacing: 0) {
                    if showToolbar {
                        MarkdownToolbar(text: $text, onUploadImage: onUploadImage)
                    }
                    TextEditor(text: $text)
                        .frame(minHeight: minHeight, maxHeight: .infinity)
                        .scrollContentBackground(.hidden)
                        .textInputAutocapitalization(.never)
                        .autocorrectionDisabled()
                        .padding(8)
                        .accessibilityIdentifier("markdown-text-editor")
                }
                .background(Color(.tertiarySystemFill))
                .clipShape(RoundedRectangle(cornerRadius: 8))
            } else {
                Group {
                    if text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                        Text("Nothing to preview")
                            .foregroundStyle(.tertiary)
                            .frame(maxWidth: .infinity, alignment: .leading)
                    } else {
                        MarkdownPreview(text: text)
                    }
                }
                .frame(minHeight: minHeight, maxHeight: .infinity, alignment: .topLeading)
                .padding(8)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(Color(.tertiarySystemFill))
                .clipShape(RoundedRectangle(cornerRadius: 8))
            }
        }
    }
}

struct MarkdownPreview: View {
    let text: String
    var baseURL: URL?
    var onNavigateToFile: ((String) -> Void)?

    var body: some View {
        let segments = MermaidParser.parse(text)
        let hasSpecialSegments = segments.contains {
            switch $0 {
            case .mermaid, .html: true
            case .text: false
            }
        }

        if hasSpecialSegments {
            VStack(alignment: .leading, spacing: 12) {
                ForEach(Array(segments.enumerated()), id: \.offset) { _, segment in
                    switch segment {
                    case let .text(markdown):
                        StructuredText(markdown: markdown, baseURL: baseURL)
                            .textual.inlineStyle(
                                InlineStyle()
                                    .code(
                                        .monospaced, .fontScale(0.85),
                                        .foregroundColor(.secondary),
                                        .backgroundColor(.secondary.opacity(0.15)),
                                    ),
                            )
                            .textual.tableStyle(.overflow)
                            .textual.overflowMode(.scroll)
                            .textual.textSelection(.enabled)
                    case let .mermaid(code):
                        MermaidDiagramView(code: code)
                    case let .html(content):
                        HTMLBlockDiagramView(
                            markdown: content,
                            baseURL: baseURL,
                            onNavigateToFile: onNavigateToFile,
                        )
                    }
                }
            }
            .environment(\.openURL, openURLAction)
        } else {
            StructuredText(markdown: text, baseURL: baseURL)
                .textual.inlineStyle(
                    InlineStyle()
                        .code(
                            .monospaced, .fontScale(0.85),
                            .foregroundColor(.secondary),
                            .backgroundColor(.secondary.opacity(0.15)),
                        ),
                )
                .textual.tableStyle(.overflow)
                .textual.overflowMode(.scroll)
                .textual.textSelection(.enabled)
                .environment(\.openURL, openURLAction)
        }
    }

    private var openURLAction: OpenURLAction {
        OpenURLAction { url in
            if let onNavigateToFile, let path = repoRelativePath(from: url) {
                onNavigateToFile(path)
                return .handled
            }
            return .systemAction
        }
    }
}

/// Extracts a repo-relative file path from a Forgejo URL.
/// URL pattern: `{serverURL}/[subpath/]{owner}/{repo}/src/branch/{ref}/{filePath}`
/// Searches for the `src/branch` segment pair to handle servers running under a subpath.
/// Returns the `filePath` portion if the URL matches, `nil` otherwise.
func repoRelativePath(from url: URL) -> String? {
    let parts = url.pathComponents.filter { $0 != "/" }
    // Find the "src" segment followed by "branch"
    guard let srcIndex = parts.indices.first(where: {
        parts[$0] == "src" && $0 + 1 < parts.count && parts[$0 + 1] == "branch"
    }),
        srcIndex + 2 < parts.count // need at least ref after "branch"
    else { return nil }
    // After "src", "branch", ref comes the file path
    let filePathStart = srcIndex + 3
    guard filePathStart < parts.count else { return nil }
    return parts.dropFirst(filePathStart).joined(separator: "/")
}

struct MermaidDiagramView: View {
    let code: String
    @State private var height: CGFloat = 200

    var body: some View {
        MermaidWebView(code: code, height: $height)
            .frame(height: height)
            .clipShape(RoundedRectangle(cornerRadius: 8))
    }
}

struct HTMLBlockDiagramView: View {
    let markdown: String
    var baseURL: URL?
    var onNavigateToFile: ((String) -> Void)?
    @State private var height: CGFloat = 100

    var body: some View {
        HTMLBlockView(
            markdown: markdown,
            height: $height,
            baseURL: baseURL,
            onNavigateToFile: onNavigateToFile,
        )
        .frame(height: height)
    }
}

#if DEBUG
    #Preview("Editor") {
        @Previewable @State var text = "# Hello\n\nThis is **bold** and *italic* text.\n\n- Item 1\n- Item 2"
        @Previewable @State var tab: EditPreviewTab = .edit
        MarkdownEditorField(text: $text, selectedTab: $tab)
            .padding()
    }

    #Preview("Preview") {
        MarkdownPreview(text: "# Hello\n\nThis is **bold** and *italic* text.\n\n```swift\nlet x = 42\n```")
            .padding()
    }
#endif
