import Cocoa
import AVFoundation
import Speech
import SwiftUI
import DynamicNotchKit

/// 应用主控制器，协调 Fn 键（长按录音）和 Control 键（双击切换模式）
final class AppDelegate: NSObject, NSApplicationDelegate, NSSpeechSynthesizerDelegate {

    /// 防止 ARC 优化释放（NSApplication.delegate 是 weak）
    private static var retainedInstance: AppDelegate?

    private let keyMonitor = KeyMonitor()
    private let speechRecognizer = SpeechRecognizer()
    private let audioMonitor = AudioLevelMonitor()
    private let recordingPanel = RecordingPanel()
    private let menuBarController = MenuBarController()
    private let llmRefiner = LLMRefiner()
    private let speechSynthesizer = NSSpeechSynthesizer()
    /// 当前正在朗读的模式，用于朗读完成后关闭对应弹窗
    private var speakingMode: LLMMode? = nil
    /// 朗读完成后的延迟关闭任务，重新输入时取消
    private var speakAutoHideWorkItem: DispatchWorkItem? = nil
    private let agentBridge: AgentBridge
    private let agentTracker = AgentProgressTracker()
    private var agentNotch: DynamicNotch<AgentNotchView>?

    private var isRecording = false
    private var finalTranscription: String = ""
    private var isTextInputActive = false
    private var agentActive = false
    private var agentIsTextMode = false
    private var agentExitingCleanly = false  // 主动关闭标记，避免误报退出错误
    private var autoHideWorkItem: DispatchWorkItem?

    override init() {
        agentBridge = AgentBridge(agentDir: Config.shared.agentDir)
        super.init()
        Self.retainedInstance = self
        speechSynthesizer.delegate = self
    }

    func applicationDidFinishLaunching(_ notification: Notification) {
        NSApp.setActivationPolicy(.accessory)
        beginPermissionFlow()
        print("[App] Ready")
    }

    // MARK: - Permissions

    private func beginPermissionFlow() {
        let hasAccessibility = CGPreflightListenEventAccess()
        let hasInputMonitoring = canCreateHIDEventTapSimple()

        // 两个权限都 OK → 直接启动
        if hasAccessibility && hasInputMonitoring {
            startKeyMonitor()
            showReadyAlert()
            return
        }

        // 打开系统设置隐私主页
        NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security")!)

        // 列出缺失的权限
        var missing: [String] = []
        if !hasAccessibility { missing.append("辅助功能") }
        if !hasInputMonitoring { missing.append("输入监控") }

        let alert = NSAlert()
        alert.messageText = "需要系统权限"
        alert.informativeText = "VoiceInput 需要以下权限才能正常工作：\n\n• \(missing.joined(separator: "\n• "))\n\n请在左侧列表中找到对应项目，点击 + 添加 VoiceInput，然后打开开关。"
        alert.alertStyle = .informational
        alert.addButton(withTitle: "已授权，继续")
        alert.addButton(withTitle: "退出")
        guard alert.runModal() == .alertFirstButtonReturn else { NSApp.terminate(nil); return }

        // 轮询检测
        Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true) { [weak self] timer in
            guard let self = self else { return }
            let ok1 = CGPreflightListenEventAccess()
            let ok2 = self.canCreateHIDEventTapSimple()
            if ok1 && ok2 {
                timer.invalidate()
                DispatchQueue.main.async {
                    print("[Permission] ✅ All permissions granted")
                    self.startKeyMonitor()
                    self.showReadyAlert()
                }
            }
        }
    }

    /// 简单检测输入监控（仅用于引导，KeyMonitor 内部有 HID→Session 回退）
    private func canCreateHIDEventTapSimple() -> Bool {
        let mask = (1 << CGEventType.flagsChanged.rawValue)
        guard let tap = CGEvent.tapCreate(tap: .cghidEventTap, place: .headInsertEventTap,
            options: .defaultTap, eventsOfInterest: CGEventMask(mask),
            callback: { _, _, _, _ in return nil }, userInfo: nil) else { return false }
        CFMachPortInvalidate(tap)
        return true
    }

    private func showReadyAlert() {
        let alert = NSAlert()
        alert.messageText = "权限已就绪"
        alert.informativeText = "Voice Input 正在运行。首次使用时还需授权麦克风和语音识别。"
        alert.alertStyle = .informational
        alert.runModal()
    }

    private func showMicrophonePermissionAlert() {
        let alert = NSAlert()
        alert.messageText = "需要麦克风权限"
        alert.informativeText = "请前往 系统设置 > 隐私与安全性 > 麦克风，开启 VoiceInput 的权限。"
        alert.alertStyle = .warning
        alert.addButton(withTitle: "打开系统设置")
        alert.addButton(withTitle: "取消")
        if alert.runModal() == .alertFirstButtonReturn {
            NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone")!)
        }
    }

    private func showSpeechPermissionAlert() {
        let alert = NSAlert()
        alert.messageText = "需要语音识别权限"
        alert.informativeText = "请前往 系统设置 > 隐私与安全性 > 语音识别，开启 VoiceInput 的权限。"
        alert.alertStyle = .warning
        alert.addButton(withTitle: "打开系统设置")
        alert.addButton(withTitle: "取消")
        if alert.runModal() == .alertFirstButtonReturn {
            NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_SpeechRecognition")!)
        }
    }

    // MARK: - Key Monitor

    private func startKeyMonitor() {
        guard keyMonitor.start() else { print("[App] Failed to start key monitor"); return }
        keyMonitor.onFnDown = { [weak self] in self?.startRecording() }
        keyMonitor.onFnUp = { [weak self] in self?.stopRecording() }
        keyMonitor.onControlDoubleClick = { [weak self] in self?.toggleModeViaDoubleClick() }
    }

    private func toggleModeViaDoubleClick() {
        let newMode: TriggerMode
        switch Config.shared.triggerMode {
        case .input:  newMode = .assist
        case .assist: newMode = .agent
        case .agent:  newMode = .input
        }
        Config.shared.triggerMode = newMode
        menuBarController.refreshModeMenu()
        print("[App] Control double-click: mode switched to \(newMode)")
    }

    // MARK: - Recording

    private func startRecording() {
        guard !isRecording else { return }

        // 如果当前模式不是智能体但刘海弹窗还在 → 关闭
        if agentNotch != nil && Config.shared.triggerMode != .agent {
            hideAgentNotch()
        }

        // 智能体 + 打字输入模式 → 直接显示文本输入刘海
        if Config.shared.triggerMode == .agent && Config.shared.agentInputMode == "text" {
            isTextInputActive = true
            recordingPanel.updateText("Type your question...")
            recordingPanel.showAnimated()
            showTextInputNotch()
            return
        }

        isRecording = true
        finalTranscription = ""

        let micStatus = AVCaptureDevice.authorizationStatus(for: .audio)
        if micStatus == .denied || micStatus == .restricted { showMicrophonePermissionAlert(); isRecording = false; return }

        let speechStatus = SFSpeechRecognizer.authorizationStatus()
        if speechStatus == .denied || speechStatus == .restricted { showSpeechPermissionAlert(); isRecording = false; return }
        if speechStatus == .notDetermined {
            SFSpeechRecognizer.requestAuthorization { [weak self] status in
                DispatchQueue.main.async {
                    self?.isRecording = false
                    guard status == .authorized else { self?.showSpeechPermissionAlert(); return }
                    self?.startRecording()
                }
            }
            return
        }

        recordingPanel.showAnimated()
        let promptText: String = {
            switch Config.shared.triggerMode {
            case .input:  return "Listening..."
            case .assist: return "Ask AI..."
            case .agent:  return "Ask Agent..."
            }
        }()
        recordingPanel.updateText(promptText)

        let language = Config.shared.language.rawValue
        Task { @MainActor in
            let audioOK = audioMonitor.start()
            if audioOK {
                audioMonitor.onLevelUpdate = { [weak self] level in
                    self?.recordingPanel.waveformView.updateLevel(level)
                }
            }
            let speechOK = await speechRecognizer.start(language: language)
            if !speechOK {
                recordingPanel.updateText("Recognition error")
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in
                    self?.cancelRecording()
                }
                return
            }
            speechRecognizer.onResult = { [weak self] text in
                guard let self, isRecording else { return }
                finalTranscription = text
                recordingPanel.updateText(text)
            }
            speechRecognizer.onError = { error in
                let ns = error as NSError
                if ns.domain == "kAFAssistantErrorDomain", ns.code == 1110 { return }
                print("[App] Speech error: \(error)")
            }
        }
    }

    private func stopRecording() {
        // 打字模式：仅隐藏录音胶囊，刘海保持打开等待输入
        if isTextInputActive {
            isTextInputActive = false
            recordingPanel.hideAnimated()
            return
        }

        guard isRecording else { return }
        isRecording = false
        audioMonitor.stop()
        _ = speechRecognizer.stop()

        let text = finalTranscription.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !text.isEmpty else { recordingPanel.hideAnimated(); finalTranscription = ""; return }

        switch Config.shared.triggerMode {
        case .input:  handleInputMode(text: text, config: Config.shared.inputLLM)
        case .assist: handleAssistMode(text: text, config: Config.shared.assistLLM)
        case .agent:  handleAgentMode(text: text)
        }
        finalTranscription = ""
    }

    private func cancelRecording() {
        audioMonitor.stop()
        speechRecognizer.cancel()
        recordingPanel.hideAnimated()
        isRecording = false
        finalTranscription = ""
    }

    // MARK: - Input Mode

    private func handleInputMode(text: String, config: LLMConfig) {
        guard config.enabled, config.isConfigured else {
            recordingPanel.hideAnimated { TextInjector.inject(text) }
            return
        }
        recordingPanel.showRefining()
        Task { @MainActor in
            do {
                let result = try await llmRefiner.refine(text: text, config: config)
                recordingPanel.hideAnimated { TextInjector.inject(result.text) }
            } catch {
                print("[App] LLM refine error: \(error)")
                recordingPanel.hideAnimated { TextInjector.inject(text) }
            }
        }
    }

    // MARK: - Assist Mode

    private func handleAssistMode(text: String, config: LLMConfig) {
        guard config.enabled, config.isConfigured else {
            recordingPanel.updateText("AI 助手未配置")
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in self?.recordingPanel.hideAnimated() }
            return
        }
        recordingPanel.showThinking()
        Task { @MainActor in
            do {
                let result = try await llmRefiner.chat(text: text, config: config)
                if Config.shared.assistOutputMode == .notch {
                    recordingPanel.hideAnimated()
                    if Config.shared.assistSpeakEnabled {
                        NotchDisplayManager.shared.show(text: result.text, autoHideAfter: nil)
                        speakingMode = .assist
                        speechSynthesizer.startSpeaking(result.text)
                    } else {
                        NotchDisplayManager.shared.show(text: result.text)
                    }
                } else {
                    recordingPanel.hideAnimated { TextInjector.inject(result.text) }
                }
            } catch {
                print("[App] LLM chat error: \(error)")
                recordingPanel.updateText("请求失败，请检查配置")
                DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in self?.recordingPanel.hideAnimated() }
            }
        }
    }

    // MARK: - Agent Mode

    private func handleAgentMode(text: String) {
        speakAutoHideWorkItem?.cancel()
        speechSynthesizer.stopSpeaking()
        speakingMode = nil
        let modelsFile = Config.shared.loadAgentModels()
        let validModels = modelsFile.models.filter { !$0.url.isEmpty && !$0.name.isEmpty && !$0.apikey.isEmpty && !$0.modes.isEmpty }
        let defaultModel = modelsFile.defaultModel.isEmpty ? validModels.first?.name : modelsFile.defaultModel
        guard !validModels.isEmpty, defaultModel != nil else {
            recordingPanel.updateText("智能体未配置")
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in self?.recordingPanel.hideAnimated() }
            return
        }
        recordingPanel.hideAnimated()

        // 如果对话已在进行中 → 追加消息
        if agentActive {
            autoHideWorkItem?.cancel()
            agentTracker.start()
            showAgentNotchInternal(isTextInput: agentIsTextMode)
            agentBridge.sendContinue(message: text)
            return
        }

        agentActive = true
        agentIsTextMode = Config.shared.agentInputMode == "text"
        agentTracker.start()
        showAgentNotchInternal(isTextInput: agentIsTextMode)

        agentBridge.onEvent = { [weak self] event in
            DispatchQueue.main.async { self?.handleAgentEvent(event) }
        }
        agentBridge.onExit = { [weak self] code in
            DispatchQueue.main.async {
                guard let self = self else { return }
                self.agentLog("[App] onExit code=\(code) currentMode=\(self.agentTracker.mode)")
                self.agentActive = false
                self.agentIsTextMode = false
                if !self.agentExitingCleanly && code != 0 && self.agentTracker.mode != "done" {
                    self.agentTracker.fail("进程异常退出 (code \(code))")
                }
                self.agentExitingCleanly = false
            }
        }
        guard agentBridge.start() else {
            agentActive = false
            agentTracker.fail("无法启动智能体进程")
            return
        }
        agentBridge.send(message: text, workspace: Config.shared.agentWorkspace,
                         allowMode: Config.shared.agentAllowMode,
                         modelKey: defaultModel!)
    }

    private func hideAgentNotch() {
        agentNotch?.hide(ignoreMouse: true)
        agentNotch = nil
    }

    private func handleAgentEvent(_ event: AgentEvent) {
        switch event {
        case .status: break  // no-op，内容切换由 toolPhaseActive 控制
        case .toolStart(let tool, let display):
            agentLog("[App] handleEvent toolStart tool=\(tool)")
            agentTracker.addTool(tool, display: display)
        case .toolEnd(let tool, let resultLines):
            agentLog("[App] handleEvent toolEnd tool=\(tool) lines=\(resultLines.count)")
            agentTracker.updateToolResult(tool, resultLines: resultLines)
        case .assistantChunk(let content):
            agentTracker.appendChunk(content)
        case .done(let content, _):
            agentLog("[App] handleEvent done contentLen=\(content.count)")
            agentTracker.finish(content: content)
            let willSpeak = Config.shared.agentSpeakEnabled && !content.isEmpty
            if willSpeak {
                speakingMode = .agent
                speechSynthesizer.startSpeaking(content)
            }
            autoHideWorkItem?.cancel()
            // 打字模式下，输入栏有内容时不自动收回；朗读模式下由朗读完成回调处理
            if !agentIsTextMode && !willSpeak {
                let workItem = DispatchWorkItem { [weak self] in self?.hideAgentNotch() }
                autoHideWorkItem = workItem
                DispatchQueue.main.asyncAfter(deadline: .now() + 20, execute: workItem)
            }
        case .error(let message):
            agentLog("[App] handleEvent error message=\(message)")
            agentTracker.fail(message)
        }
    }

    private func agentLog(_ msg: String) {
        let logPath = (Bundle.main.bundlePath as NSString).deletingLastPathComponent + "/../../../agent_debug.log"
        let ts = ISO8601DateFormatter().string(from: Date())
        let line = "[\(ts)] \(msg)\n"
        if let handle = FileHandle(forWritingAtPath: logPath) {
            handle.seekToEndOfFile()
            handle.write(line.data(using: .utf8)!)
            handle.closeFile()
        }
    }

    private func showAgentNotch() {
        showAgentNotchInternal(isTextInput: false)
    }

    private func showTextInputNotch() {
        // 重置显示状态，但不进入 thinking（等用户发送后再开始）
        agentTracker.resetForInput()
        agentIsTextMode = true
        showAgentNotchInternal(isTextInput: true)
    }

    private func showAgentNotchInternal(isTextInput: Bool) {
        agentTracker.isTextInputMode = isTextInput

        // 弹窗已存在 → 复用，只刷新显示
        if agentNotch != nil {
            agentNotch?.show()
            return
        }

        // 创建新弹窗
        let view = AgentNotchView(
            tracker: agentTracker,
            onCopy: {
                NSPasteboard.general.clearContents()
                NSPasteboard.general.setString(self.agentTracker.finalContent, forType: .string)
            },
            onStop: { [weak self] in
                self?.speakingMode = nil
                self?.speakAutoHideWorkItem?.cancel()
                self?.speechSynthesizer.stopSpeaking()
                self?.autoHideWorkItem?.cancel()
                self?.agentExitingCleanly = true
                self?.agentBridge.stop()
                self?.hideAgentNotch()
            },
            onClose: { [weak self] in
                self?.speakingMode = nil
                self?.speakAutoHideWorkItem?.cancel()
                self?.speechSynthesizer.stopSpeaking()
                self?.autoHideWorkItem?.cancel()
                self?.hideAgentNotch()
            },
            onNewConversation: { [weak self] in
                self?.speakingMode = nil
                self?.speakAutoHideWorkItem?.cancel()
                self?.speechSynthesizer.stopSpeaking()
                self?.autoHideWorkItem?.cancel()
                self?.agentExitingCleanly = true
                self?.agentBridge.stop()
                self?.agentActive = false
                self?.agentIsTextMode = false
                self?.agentTracker.resetForInput()
            },
            onSendText: { [weak self] text in
                self?.handleTextInput(text)
            },
            onContentSizeChanged: { [weak self] in
                self?.agentNotch?.notifyContentChanged()
            }
        )
        agentNotch = DynamicNotch(style: .notch) { view }
        agentNotch?.show()
    }

    private func handleTextInput(_ text: String) {
        handleAgentMode(text: text)
    }

    func applicationWillTerminate(_ notification: Notification) {
        speechSynthesizer.stopSpeaking()
        keyMonitor.stop()
        audioMonitor.stop()
        speechRecognizer.cancel()
        recordingPanel.close()
    }

    // MARK: - NSSpeechSynthesizerDelegate

    func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) {
        guard finishedSpeaking else { return }
        DispatchQueue.main.async { [weak self] in
            guard let self, let mode = self.speakingMode else { return }
            self.speakingMode = nil

            // 朗读完成后等待 5 秒再带动画关闭弹窗
            let workItem = DispatchWorkItem { [weak self] in
                switch mode {
                case .assist:
                    NotchDisplayManager.shared.extendAutoHide(to: 0.1)
                case .agent:
                    // 打字模式下不自动关闭，用户可能还在输入框中
                    if self?.agentIsTextMode != true {
                        self?.agentNotch?.show(for: 0.1)
                    }
                default:
                    break
                }
            }
            self.speakAutoHideWorkItem = workItem
            DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: workItem)
        }
    }
}
