import Foundation
import PhotosUI
import SwiftUI
import UniformTypeIdentifiers

struct MarkdownToolbar: View {
    @Binding var text: String
    var onUploadImage: ((Data, String, String) async throws -> String)?

    @State private var photosItem: PhotosPickerItem?
    @State private var showPhotosPicker = false
    @State private var showFileImporter = false
    @State private var isUploading = false
    @State private var uploadError: String?
    @State private var showUploadError = false

    var body: some View {
        // A wrapping flow layout keeps every icon visible: when they don't fit on
        // one line they wrap to the next, instead of being hidden off-screen behind
        // a horizontal scroll view.
        MarkdownToolbarFlow(spacing: 10) {
            toolbarButton("bold", icon: "bold", label: "Bold") { wrap("**") }
            toolbarButton("italic", icon: "italic", label: "Italic") { wrap("_") }
            toolbarButton("heading", icon: "number", label: "Heading") { prefix("# ") }
            toolbarButton("code", icon: "chevron.left.forwardslash.chevron.right",
                          label: "Inline code") { wrap("`") }
            toolbarButton("codeblock", icon: "text.page", label: "Code block") { wrapBlock("```") }
            toolbarButton("link", icon: "link", label: "Insert link") { insertLink() }
            toolbarButton("list", icon: "list.bullet", label: "Bulleted list") { prefix("- ") }
            toolbarButton("quote", icon: "text.quote", label: "Quote") { prefix("> ") }
            toolbarButton("task", icon: "checklist", label: "Task list") { prefix("- [ ] ") }

            if onUploadImage != nil {
                uploadControl
            }
        }
        .padding(.horizontal, 8)
        .padding(.vertical, 6)
        .frame(maxWidth: .infinity, alignment: .leading)
        .alert("Upload Failed", isPresented: $showUploadError) {
            Button("OK", role: .cancel) {}
        } message: {
            Text(uploadError ?? "")
        }
    }

    @ViewBuilder
    private var uploadControl: some View {
        if isUploading {
            ProgressView()
                .controlSize(.small)
                .frame(width: 28, height: 28)
        } else if ProcessInfo.processInfo.arguments.contains("-dev_testImageUpload") {
            // In UI tests, bypass the pickers and upload hardcoded files directly.
            Button { Task { await uploadTestImage() } } label: {
                toolbarIcon("photo")
            }
            .buttonStyle(.plain)
            .foregroundStyle(.primary)
            .accessibilityLabel("Attach image")
            .accessibilityIdentifier("markdown-toolbar-image")

            Button { Task { await uploadTestFile() } } label: {
                toolbarIcon("doc")
            }
            .buttonStyle(.plain)
            .foregroundStyle(.primary)
            .accessibilityLabel("Attach file")
            .accessibilityIdentifier("markdown-toolbar-file")
        } else {
            Menu {
                Button { showPhotosPicker = true } label: {
                    Label("Photo Library", systemImage: "photo")
                }
                Button { showFileImporter = true } label: {
                    Label("Choose File", systemImage: "doc")
                }
            } label: {
                toolbarIcon("paperclip")
            }
            .buttonStyle(.plain)
            .foregroundStyle(.primary)
            .accessibilityLabel("Attach")
            .accessibilityIdentifier("markdown-toolbar-attach")
            .photosPicker(isPresented: $showPhotosPicker, selection: $photosItem, matching: .images)
            .onChange(of: photosItem) { _, item in
                guard let item else { return }
                Task { await uploadPickedImage(item) }
            }
            .fileImporter(
                isPresented: $showFileImporter,
                allowedContentTypes: [.item],
                allowsMultipleSelection: false,
            ) { result in
                Task { await uploadPickedFile(result) }
            }
        }
    }

    private func toolbarIcon(_ systemName: String) -> some View {
        Image(systemName: systemName)
            .font(.subheadline)
            .frame(width: 28, height: 28)
            .contentShape(Rectangle())
    }

    private func uploadTestImage() async {
        guard let onUploadImage else { return }
        // 1×1 transparent PNG, used in UI tests to skip the Photos picker.
        let base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
        guard let pngData = Data(base64Encoded: base64) else { return }
        await runUpload { try await onUploadImage(pngData, "test.png", "image/png") }
    }

    private func uploadTestFile() async {
        guard let onUploadImage else { return }
        // Plain-text log file, used in UI tests to exercise the non-image
        // attachment path (link markdown + file-row display).
        let logData = Data("line1 ERROR boom\nline2 WARN stuff\nline3 done\n".utf8)
        await runUpload { try await onUploadImage(logData, "server.log", "text/plain") }
    }

    private func runUpload(_ upload: () async throws -> String) async {
        isUploading = true
        defer { isUploading = false }
        do {
            let markdown = try await upload()
            if text.isEmpty || text.hasSuffix("\n") {
                text.append(markdown)
            } else {
                text.append("\n\(markdown)")
            }
        } catch {
            uploadError = error.localizedDescription
            showUploadError = true
        }
    }

    private func uploadPickedFile(_ result: Result<[URL], Error>) async {
        guard let onUploadImage else { return }
        do {
            let urls = try result.get()
            guard let url = urls.first else { return }
            // Files returned by the document picker live outside the app sandbox and
            // must be accessed through a security-scoped resource.
            let didAccess = url.startAccessingSecurityScopedResource()
            defer { if didAccess { url.stopAccessingSecurityScopedResource() } }
            let data = try Data(contentsOf: url)
            let fileName = url.lastPathComponent
            let mimeType = UTType(filenameExtension: url.pathExtension)?.preferredMIMEType
                ?? "application/octet-stream"
            await runUpload { try await onUploadImage(data, fileName, mimeType) }
        } catch {
            uploadError = error.localizedDescription
            showUploadError = true
        }
    }

    private func uploadPickedImage(_ item: PhotosPickerItem) async {
        guard let onUploadImage else { return }
        isUploading = true
        defer {
            isUploading = false
            photosItem = nil
        }
        do {
            guard let data = try await item.loadTransferable(type: Data.self) else { return }
            let mimeType = item.supportedContentTypes.first?.preferredMIMEType ?? "image/jpeg"
            let ext = item.supportedContentTypes.first?.preferredFilenameExtension ?? "jpg"
            let markdown = try await onUploadImage(data, "image.\(ext)", mimeType)
            if text.isEmpty || text.hasSuffix("\n") {
                text.append(markdown)
            } else {
                text.append("\n\(markdown)")
            }
        } catch {
            uploadError = error.localizedDescription
            showUploadError = true
        }
    }

    private func toolbarButton(_ id: String, icon: String, label: String, action: @escaping () -> Void) -> some View {
        Button(action: action) {
            toolbarIcon(icon)
        }
        .buttonStyle(.plain)
        .foregroundStyle(.primary)
        .accessibilityLabel(label)
        .accessibilityIdentifier("markdown-toolbar-\(id)")
    }

    private func wrap(_ marker: String) {
        text.append("\(marker)text\(marker)")
    }

    private func prefix(_ marker: String) {
        if text.isEmpty || text.hasSuffix("\n") {
            text.append(marker)
        } else {
            text.append("\n\(marker)")
        }
    }

    private func wrapBlock(_ fence: String) {
        if text.isEmpty || text.hasSuffix("\n") {
            text.append("\(fence)\n\n\(fence)")
        } else {
            text.append("\n\(fence)\n\n\(fence)")
        }
    }

    private func insertLink() {
        text.append("[title](url)")
    }
}
