import AppKit
import Foundation
import UniformTypeIdentifiers

@MainActor
final class NormalGeneratorStore: ObservableObject {
    @Published var source: TextureAsset?
    @Published var settings = NormalGeneratorSettings()
    @Published var previews: [NormalPreviewKind: NSImage] = [:]
    @Published var logLines: [String] = [
        "Load one source texture to generate Normal, Roughness, Gloss, Metallic, AO and ORM maps."
    ]
    @Published var lastExportURL: URL?

    private let processor = TextureProcessor()

    var canExport: Bool {
        source != nil
    }

    func chooseSourceTexture() {
        let panel = NSOpenPanel()
        panel.title = "Choose Source Texture"
        panel.prompt = "Use Texture"
        panel.allowsMultipleSelection = false
        panel.canChooseDirectories = false
        panel.canChooseFiles = true
        panel.allowedContentTypes = [.png, .jpeg, .tiff, .bmp, .gif, .heic]

        if panel.runModal() == .OK, let url = panel.url {
            importSourceTexture(url)
        }
    }

    func importSourceTexture(_ url: URL) {
        do {
            let asset = try processor.loadAsset(from: url)
            source = asset

            if settings.materialName == "BREDA_Normal" {
                settings.materialName = url.deletingPathExtension().lastPathComponent
            }

            appendLog("Source loaded: \(asset.fileName), \(asset.sizeLabel)")
            buildPreview()
        } catch {
            appendLog("Load failed: \(error.localizedDescription)")
        }
    }

    func removeSourceTexture() {
        source = nil
        previews.removeAll()
        appendLog("Source removed.")
    }

    func reset() {
        source = nil
        previews.removeAll()
        lastExportURL = nil
        settings = NormalGeneratorSettings()
        logLines = ["Inputs cleared."]
    }

    func buildPreview() {
        do {
            previews = try processor.buildNormalPreviews(source: source, settings: settings)
            if !previews.isEmpty {
            appendLog("Generated map preview updated.")
            }
        } catch {
            appendLog("Preview failed: \(error.localizedDescription)")
        }
    }

    func exportSet() {
        guard canExport else {
            appendLog("Export blocked: source texture is required.")
            return
        }

        let panel = NSOpenPanel()
        panel.title = "Choose Export Folder"
        panel.prompt = "Export Here"
        panel.canChooseFiles = false
        panel.canChooseDirectories = true
        panel.canCreateDirectories = true
        panel.allowsMultipleSelection = false

        guard panel.runModal() == .OK, let directoryURL = panel.url else {
            return
        }

        do {
            let result = try processor.exportGeneratedNormalSet(
                source: source,
                settings: settings,
                parentDirectory: directoryURL
            )
            lastExportURL = result.folderURL
            appendLog("Export complete: \(result.folderURL.path)")
            result.files.forEach { appendLog("Wrote \($0)") }
        } catch {
            appendLog("Export failed: \(error.localizedDescription)")
        }
    }

    func acceptDrop(_ providers: [NSItemProvider]) -> Bool {
        guard let provider = providers.first(where: { $0.hasItemConformingToTypeIdentifier(UTType.fileURL.identifier) }) else {
            return false
        }

        provider.loadItem(forTypeIdentifier: UTType.fileURL.identifier, options: nil) { [weak self] item, _ in
            let url: URL?
            if let droppedURL = item as? URL {
                url = droppedURL
            } else if let data = item as? Data {
                url = URL(dataRepresentation: data, relativeTo: nil)
            } else {
                url = nil
            }

            guard let url else {
                return
            }

            Task { @MainActor in
                self?.importSourceTexture(url)
            }
        }

        return true
    }

    private func appendLog(_ line: String) {
        let timestamp = Self.timestampFormatter.string(from: Date())
        logLines.insert("[\(timestamp)] \(line)", at: 0)
        if logLines.count > 80 {
            logLines.removeLast(logLines.count - 80)
        }
    }

    private static let timestampFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm:ss"
        return formatter
    }()
}
