import Foundation
import AppKit
import UniformTypeIdentifiers
import KeyboardShortcuts
import LaunchAtLogin
import SwiftData

private final class BackupOptions: NSObject {
    let view: NSView

    private let allButton: NSButton
    private let individualButton: NSButton
    private let categoryButtons: [BackupCategory: NSButton]

    override init() {
        self.view = NSView(frame: NSRect(x: 0, y: 0, width: 360, height: 188))
        self.allButton = NSButton(radioButtonWithTitle: "Alles", target: nil, action: nil)
        self.individualButton = NSButton(radioButtonWithTitle: "Afzonderlijke categorieën", target: nil, action: nil)

        var buttons: [BackupCategory: NSButton] = [:]
        for category in BackupCategory.allCases {
            let button = NSButton(checkboxWithTitle: category.title, target: nil, action: nil)
            button.state = .on
            button.isEnabled = false
            buttons[category] = button
        }
        self.categoryButtons = buttons

        super.init()

        allButton.state = .on
        individualButton.state = .off
        allButton.target = self
        allButton.action = #selector(modeChanged(_:))
        individualButton.target = self
        individualButton.action = #selector(modeChanged(_:))

        let stack = NSStackView()
        stack.orientation = .vertical
        stack.alignment = .leading
        stack.spacing = 8
        stack.translatesAutoresizingMaskIntoConstraints = false

        let categoryStack = NSStackView()
        categoryStack.orientation = .vertical
        categoryStack.alignment = .leading
        categoryStack.spacing = 6
        categoryStack.translatesAutoresizingMaskIntoConstraints = false

        for category in BackupCategory.allCases {
            guard let button = categoryButtons[category] else { continue }
            button.target = self
            button.action = #selector(categoryChanged(_:))
            categoryStack.addArrangedSubview(button)
        }

        view.addSubview(stack)
        view.addSubview(categoryStack)
        stack.addArrangedSubview(allButton)
        stack.addArrangedSubview(individualButton)

        NSLayoutConstraint.activate([
            stack.topAnchor.constraint(equalTo: view.topAnchor, constant: 4),
            stack.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            stack.trailingAnchor.constraint(equalTo: view.trailingAnchor),

            categoryStack.topAnchor.constraint(equalTo: individualButton.bottomAnchor, constant: 6),
            categoryStack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 18),
            categoryStack.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            categoryStack.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor)
        ])
    }

    var selectedCategories: Set<BackupCategory> {
        if allButton.state == .on {
            return Set(BackupCategory.allCases)
        }

        return Set(categoryButtons.compactMap { category, button in
            button.state == .on ? category : nil
        })
    }

    @objc private func modeChanged(_ sender: NSButton) {
        let useAll = sender == allButton
        allButton.state = useAll ? .on : .off
        individualButton.state = useAll ? .off : .on
        setCategoryButtonsEnabled(!useAll)
    }

    @objc private func categoryChanged(_ sender: NSButton) {
        guard individualButton.state != .on else { return }
        allButton.state = .off
        individualButton.state = .on
        setCategoryButtonsEnabled(true)
    }

    private func setCategoryButtonsEnabled(_ isEnabled: Bool) {
        for button in categoryButtons.values {
            button.isEnabled = isEnabled
        }
    }
}

class ImportExportService {
    static let shared = ImportExportService()
    private let currentSettingsVersion: String

    private let keyIsAudioCleanupEnabled = "IsAudioCleanupEnabled"
    private let keyIsTranscriptionCleanupEnabled = "IsTranscriptionCleanupEnabled"
    private let keyTranscriptionRetentionMinutes = "TranscriptionRetentionMinutes"
    private let keyAudioRetentionPeriod = "AudioRetentionPeriod"

    private let keyIsTextFormattingEnabled = "IsTextFormattingEnabled"
    private let keyRemovePunctuation = "RemovePunctuation"
    private let keyLowercaseTranscription = "LowercaseTranscription"

    private init() {
        if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String {
            self.currentSettingsVersion = version
        } else {
            self.currentSettingsVersion = "0.0.0"
        }
    }

    @MainActor
    func exportSettings(enhancementService: AIEnhancementService, whisperPrompt: WhisperPrompt, hotkeyManager: HotkeyManager, menuBarManager: MenuBarManager, mediaController: MediaController, playbackController: PlaybackController, soundManager: SoundManager, recorderUIManager: RecorderUIManager, modelContext: ModelContext) {
        let powerModeManager = PowerModeManager.shared
        let emojiManager = EmojiManager.shared

        let exportablePrompts = enhancementService.customPrompts.filter { !$0.isPredefined }

        let powerConfigs = powerModeManager.configurations
        let powerModeShortcuts = Dictionary(uniqueKeysWithValues: powerConfigs.compactMap { config -> (String, KeyboardShortcuts.Shortcut)? in
            guard config.hotkeyShortcut != nil else { return nil }
            guard let shortcut = KeyboardShortcuts.getShortcut(for: .powerMode(id: config.id)) else { return nil }
            return (config.id.uuidString, shortcut)
        })

        // Export custom models
        let customModels = CustomCloudModelManager.shared.customModels.map { CustomModelBackup(model: $0) }

        // Fetch vocabulary words from SwiftData
        var exportedDictionaryItems: [WordBackup]? = nil
        let vocabularyDescriptor = FetchDescriptor<VocabularyWord>()
        if let items = try? modelContext.fetch(vocabularyDescriptor), !items.isEmpty {
            exportedDictionaryItems = items.map { WordBackup(word: $0.word) }
        }

        // Fetch word replacements from SwiftData
        var exportedWordReplacements: [String: String]? = nil
        let replacementsDescriptor = FetchDescriptor<WordReplacement>()
        if let replacements = try? modelContext.fetch(replacementsDescriptor), !replacements.isEmpty {
            exportedWordReplacements = Dictionary(replacements.map { ($0.originalText, $0.replacementText) }, uniquingKeysWith: { _, last in last })
        }

        let generalSettingsToExport = GeneralBackup(
            toggleMiniRecorderShortcut: KeyboardShortcuts.getShortcut(for: .toggleMiniRecorder),
            toggleMiniRecorderShortcut2: KeyboardShortcuts.getShortcut(for: .toggleMiniRecorder2),
            pasteLastTranscriptionShortcut: KeyboardShortcuts.getShortcut(for: .pasteLastTranscription),
            pasteLastEnhancementShortcut: KeyboardShortcuts.getShortcut(for: .pasteLastEnhancement),
            retryLastTranscriptionShortcut: KeyboardShortcuts.getShortcut(for: .retryLastTranscription),
            cancelRecorderShortcut: KeyboardShortcuts.getShortcut(for: .cancelRecorder),
            openHistoryWindowShortcut: KeyboardShortcuts.getShortcut(for: .openHistoryWindow),
            quickAddToDictionaryShortcut: KeyboardShortcuts.getShortcut(for: .quickAddToDictionary),
            toggleEnhancementShortcut: KeyboardShortcuts.getShortcut(for: .toggleEnhancement),
            selectedHotkey1RawValue: hotkeyManager.selectedHotkey1.rawValue,
            selectedHotkey2RawValue: hotkeyManager.selectedHotkey2.rawValue,
            hotkeyMode1RawValue: hotkeyManager.hotkeyMode1.rawValue,
            hotkeyMode2RawValue: hotkeyManager.hotkeyMode2.rawValue,
            isMiddleClickToggleEnabled: hotkeyManager.isMiddleClickToggleEnabled,
            middleClickActivationDelay: hotkeyManager.middleClickActivationDelay,
            launchAtLoginEnabled: LaunchAtLogin.isEnabled,
            isMenuBarOnly: menuBarManager.isMenuBarOnly,
            recorderType: recorderUIManager.recorderType,
            isTranscriptionCleanupEnabled: UserDefaults.standard.bool(forKey: keyIsTranscriptionCleanupEnabled),
            transcriptionRetentionMinutes: UserDefaults.standard.integer(forKey: keyTranscriptionRetentionMinutes),
            isAudioCleanupEnabled: UserDefaults.standard.bool(forKey: keyIsAudioCleanupEnabled),
            audioRetentionPeriod: UserDefaults.standard.integer(forKey: keyAudioRetentionPeriod),

            isSoundFeedbackEnabled: soundManager.isEnabled,
            isSystemMuteEnabled: mediaController.isSystemMuteEnabled,
            isPauseMediaEnabled: playbackController.isPauseMediaEnabled,
            audioResumptionDelay: mediaController.audioResumptionDelay,
            isTextFormattingEnabled: UserDefaults.standard.bool(forKey: keyIsTextFormattingEnabled),
            removePunctuation: UserDefaults.standard.bool(forKey: keyRemovePunctuation),
            lowercaseTranscription: UserDefaults.standard.bool(forKey: keyLowercaseTranscription),
            isExperimentalFeaturesEnabled: UserDefaults.standard.bool(forKey: "isExperimentalFeaturesEnabled"),
            restoreClipboardAfterPaste: UserDefaults.standard.bool(forKey: "restoreClipboardAfterPaste"),
            clipboardRestoreDelay: UserDefaults.standard.double(forKey: "clipboardRestoreDelay"),
            useAppleScriptPaste: UserDefaults.standard.bool(forKey: "useAppleScriptPaste")
        )

        let exportedSettings = BackupFile(
            version: currentSettingsVersion,
            customPrompts: exportablePrompts,
            powerModeConfigs: powerConfigs,
            powerModeShortcuts: powerModeShortcuts.isEmpty ? nil : powerModeShortcuts,
            vocabularyWords: exportedDictionaryItems,
            wordReplacements: exportedWordReplacements,
            generalSettings: generalSettingsToExport,
            customEmojis: emojiManager.customEmojis,
            customCloudModels: customModels
        )

        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted

        do {
            let jsonData = try encoder.encode(exportedSettings)

            let savePanel = NSSavePanel()
            savePanel.allowedContentTypes = [UTType.json]
            savePanel.nameFieldStringValue = "Proat_Instellingen_Backup.json"
            savePanel.title = "Proat-instellingen exporteren"
            savePanel.message = "Kies een locatie om je instellingen op te slaan."

            DispatchQueue.main.async {
                if savePanel.runModal() == .OK {
                    if let url = savePanel.url {
                        do {
                            try jsonData.write(to: url)
                            self.showAlert(title: "Export geslaagd", message: "Je instellingen zijn geëxporteerd naar \(url.lastPathComponent).")
                        } catch {
                            self.showAlert(title: "Exportfout", message: "Kon de instellingen niet naar het bestand opslaan: \(error.localizedDescription)")
                        }
                    }
                } else {
                    self.showAlert(title: "Export geannuleerd", message: "Het exporteren van de instellingen is geannuleerd.")
                }
            }
        } catch {
            self.showAlert(title: "Exportfout", message: "Kon de instellingen niet naar JSON coderen: \(error.localizedDescription)")
        }
    }

    @MainActor
    func importSettings(enhancementService: AIEnhancementService, whisperPrompt: WhisperPrompt, hotkeyManager: HotkeyManager, menuBarManager: MenuBarManager, mediaController: MediaController, playbackController: PlaybackController, soundManager: SoundManager, recorderUIManager: RecorderUIManager, modelContext: ModelContext, transcriptionModelManager: TranscriptionModelManager) {
        let openPanel = NSOpenPanel()
        openPanel.allowedContentTypes = [UTType.json]
        openPanel.canChooseFiles = true
        openPanel.canChooseDirectories = false
        openPanel.allowsMultipleSelection = false
        openPanel.title = "Proat-instellingen importeren"
        openPanel.message = "Kies een instellingenback-up en selecteer daarna wat je wilt importeren."

        guard openPanel.runModal() == .OK else {
            showAlert(title: "Import geannuleerd", message: "Het importeren van de instellingen is geannuleerd.")
            return
        }

        guard let url = openPanel.url else {
            showAlert(title: "Importfout", message: "Kon de bestandslocatie niet ophalen uit het venster.")
            return
        }

        do {
            let jsonData = try Data(contentsOf: url)
            let decoder = JSONDecoder()
            let backup = try decoder.decode(BackupFile.self, from: jsonData)

            if backup.version != currentSettingsVersion {
                showAlert(title: "Versie komt niet overeen", message: "Het geïmporteerde instellingenbestand (versie \(backup.version)) komt van een andere versie dan je app (versie \(currentSettingsVersion)). De import wordt voortgezet, maar houd rekening met mogelijke incompatibiliteiten.")
            }

            guard let selectedCategories = presentImportSelectionDialog() else {
                showAlert(title: "Import geannuleerd", message: "Er zijn geen instellingen geïmporteerd.")
                return
            }

            guard !selectedCategories.isEmpty else {
                showAlert(title: "Importfout", message: "Selecteer ten minste één categorie om te importeren.")
                return
            }

            try BackupImporter.apply(
                backup,
                categories: selectedCategories,
                enhancementService: enhancementService,
                hotkeyManager: hotkeyManager,
                menuBarManager: menuBarManager,
                mediaController: mediaController,
                playbackController: playbackController,
                soundManager: soundManager,
                recorderUIManager: recorderUIManager,
                modelContext: modelContext,
                transcriptionModelManager: transcriptionModelManager
            )

            showImportSuccessAlert(
                message: "Instellingen succesvol geïmporteerd uit \(url.lastPathComponent).\n\nGeïmporteerd: \(categorySummary(for: selectedCategories)).",
                needsAPIKeyReminder: needsAPIKeyReminder(for: selectedCategories)
            )
        } catch {
            showAlert(title: "Importfout", message: "Fout bij het importeren van instellingen: \(error.localizedDescription). Het bestand is mogelijk beschadigd of heeft niet de juiste indeling.")
        }
    }

    private func presentImportSelectionDialog() -> Set<BackupCategory>? {
        let accessory = BackupOptions()
        let alert = NSAlert()
        alert.messageText = "Instellingen importeren"
        alert.informativeText = "Kies wat je uit deze back-up wilt importeren."
        alert.alertStyle = .informational
        alert.accessoryView = accessory.view
        alert.addButton(withTitle: "Importeren")
        alert.addButton(withTitle: "Annuleren")

        let response = alert.runModal()
        guard response == .alertFirstButtonReturn else {
            return nil
        }

        return accessory.selectedCategories
    }

    private func categorySummary(for categories: Set<BackupCategory>) -> String {
        if categories == Set(BackupCategory.allCases) {
            return "Alle instellingen"
        }

        return BackupCategory.allCases
            .filter { categories.contains($0) }
            .map(\.title)
            .joined(separator: ", ")
    }

    private func needsAPIKeyReminder(for categories: Set<BackupCategory>) -> Bool {
        !categories.isDisjoint(with: [.prompts, .powerMode, .customModels])
    }

    private func showAlert(title: String, message: String) {
        DispatchQueue.main.async {
            let alert = NSAlert()
            alert.messageText = title
            alert.informativeText = message
            alert.alertStyle = .informational
            alert.addButton(withTitle: "OK")
            alert.runModal()
        }
    }

    private func showImportSuccessAlert(message: String, needsAPIKeyReminder: Bool) {
        DispatchQueue.main.async {
            let alert = NSAlert()
            alert.messageText = "Import geslaagd"
            var informativeText = message
            if needsAPIKeyReminder {
                informativeText += "\n\nBELANGRIJK: Als je AI-verbeteringsfuncties gebruikte, stel dan je API-sleutels opnieuw in bij de sectie Verbetering."
            }
            informativeText += "\n\nHet wordt aanbevolen om Proat opnieuw te starten zodat alle wijzigingen volledig actief worden."
            alert.informativeText = informativeText
            alert.alertStyle = .informational
            alert.addButton(withTitle: "OK")
            if needsAPIKeyReminder {
                alert.addButton(withTitle: "API-sleutels instellen")
            }
            
            let response = alert.runModal()
            if needsAPIKeyReminder && response == .alertSecondButtonReturn {
                NotificationCenter.default.post(
                    name: .navigateToDestination,
                    object: nil,
                    userInfo: ["destination": "Enhancement"]
                )
            }
        }
    }
}
