import Cocoa

/// LLM 设置窗口（支持双模式）
final class SettingsWindow: NSWindow {

    private let mode: LLMMode
    private let baseURLField: NSTextField
    private let apiKeyField: NSSecureTextField
    private let modelField: NSTextField
    private let testButton: NSButton
    private let saveButton: NSButton
    private let copyFromOtherButton: NSButton
    private let statusLabel: NSTextField

    // Assist 模式专属：输出方式
    private let outputModeLabel: NSTextField
    private let outputModePopup: NSPopUpButton

    // Assist 模式专属：朗读开关
    private let speakCheckbox: NSButton

    // Agent 模式专属：朗读开关
    private let agentSpeakCheckbox: NSButton

    // Agent 模式专属
    private let workspaceField: NSTextField
    private let browseButton: NSButton
    private let modelPopup: NSPopUpButton
    private let allowModePopup: NSPopUpButton
    private let inputModePopup: NSPopUpButton
    private let thinkingModePopup: NSPopUpButton
    private let tavilyKeyField: NSSecureTextField
    private let modelTableView: NSTableView
    private let addModelButton: NSButton
    private let editModelButton: NSButton
    private let deleteModelButton: NSButton
    private var agentModelList: [AgentModelConfig] = []
    private var agentUIBuilt = false

    init(mode: LLMMode) {
        self.mode = mode
        
        baseURLField = NSTextField(frame: .zero)
        apiKeyField = NSSecureTextField(frame: .zero)
        modelField = NSTextField(frame: .zero)
        testButton = NSButton(title: "Test", target: nil, action: nil)
        saveButton = NSButton(title: "Save", target: nil, action: nil)
        copyFromOtherButton = NSButton(title: "", target: nil, action: nil)
        statusLabel = NSTextField(labelWithString: "")
        outputModeLabel = NSTextField(labelWithString: "")
        outputModePopup = NSPopUpButton(frame: .zero, pullsDown: false)
        speakCheckbox = NSButton(checkboxWithTitle: "朗读 AI 回复", target: nil, action: nil)
        agentSpeakCheckbox = NSButton(checkboxWithTitle: "任务完成时朗读 AI 最终输出", target: nil, action: nil)
        workspaceField = NSTextField(frame: .zero)
        browseButton = NSButton(title: "浏览...", target: nil, action: nil)
        modelPopup = NSPopUpButton(frame: .zero, pullsDown: false)
        allowModePopup = NSPopUpButton(frame: .zero, pullsDown: false)
        inputModePopup = NSPopUpButton(frame: .zero, pullsDown: false)
        thinkingModePopup = NSPopUpButton(frame: .zero, pullsDown: false)
        tavilyKeyField = NSSecureTextField(frame: .zero)
        modelTableView = NSTableView(frame: .zero)
        addModelButton = NSButton(title: "+", target: nil, action: nil)
        editModelButton = NSButton(title: "编辑", target: nil, action: nil)
        deleteModelButton = NSButton(title: "删除", target: nil, action: nil)

        let height: CGFloat = mode == .agent ? 700 : (mode == .assist ? 400 : 320)
        let width: CGFloat = mode == .agent ? 500 : 420
        let windowRect = NSRect(x: 0, y: 0, width: width, height: height)
        super.init(
            contentRect: windowRect,
            styleMask: [.titled, .closable, .miniaturizable],
            backing: .buffered,
            defer: false
        )

        configureWindow()
        buildUI()
        loadCurrentConfig()
    }

    private func configureWindow() {
        switch mode {
        case .input:  title = "Input LLM Settings (Fn)"
        case .assist: title = "Assist LLM Settings (Control)"
        case .agent:  title = "Agent LLM Settings"
        }
        isReleasedWhenClosed = false
        center()
    }

    // MARK: - 快捷键支持

    override func performKeyEquivalent(with event: NSEvent) -> Bool {
        guard event.modifierFlags.contains(.command) else {
            return super.performKeyEquivalent(with: event)
        }

        let selector: Selector?
        switch event.charactersIgnoringModifiers?.lowercased() {
        case "a": selector = #selector(NSText.selectAll(_:))
        case "c": selector = Selector(("copy:"))
        case "v": selector = Selector(("paste:"))
        case "x": selector = Selector(("cut:"))
        default: selector = nil
        }

        guard let sel = selector else {
            return super.performKeyEquivalent(with: event)
        }

        if NSApp.sendAction(sel, to: nil, from: self) {
            return true
        }

        return super.performKeyEquivalent(with: event)
    }

    private func makeLabel(_ text: String) -> NSTextField {
        let label = NSTextField(labelWithString: text)
        label.font = .systemFont(ofSize: 11, weight: .semibold)
        label.textColor = .secondaryLabelColor
        label.alignment = .right
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }

    private func buildUI() {
        if mode == .agent {
            buildAgentUI()
            return
        }

        guard let content = contentView else { return }

        let baseURLLabel = makeLabel("API Base URL:")
        let apiKeyLabel = makeLabel("API Key:")
        let modelLabel = makeLabel("Model:")

        baseURLField.placeholderString = "https://api.openai.com"
        baseURLField.font = .systemFont(ofSize: 13)
        baseURLField.bezelStyle = .roundedBezel
        baseURLField.translatesAutoresizingMaskIntoConstraints = false

        apiKeyField.placeholderString = "sk-..."
        apiKeyField.font = .systemFont(ofSize: 13)
        apiKeyField.bezelStyle = .roundedBezel
        apiKeyField.translatesAutoresizingMaskIntoConstraints = false

        modelField.placeholderString = mode == .input ? "deepseek-v4-flash" : "gpt-4"
        modelField.font = .systemFont(ofSize: 13)
        modelField.bezelStyle = .roundedBezel
        modelField.translatesAutoresizingMaskIntoConstraints = false

        testButton.bezelStyle = .rounded
        testButton.translatesAutoresizingMaskIntoConstraints = false
        testButton.target = self
        testButton.action = #selector(testConnection)

        saveButton.bezelStyle = .rounded
        saveButton.translatesAutoresizingMaskIntoConstraints = false
        saveButton.keyEquivalent = "\r"
        saveButton.target = self
        saveButton.action = #selector(saveConfig)

        let otherModeName = mode == .input ? "AI 助手" : "语音输入"
        copyFromOtherButton.title = "从「\(otherModeName)」复制配置"
        copyFromOtherButton.bezelStyle = .rounded
        copyFromOtherButton.translatesAutoresizingMaskIntoConstraints = false
        copyFromOtherButton.target = self
        copyFromOtherButton.action = #selector(copyFromOther)

        statusLabel.font = .systemFont(ofSize: 11)
        statusLabel.textColor = .secondaryLabelColor
        statusLabel.alignment = .center
        statusLabel.maximumNumberOfLines = 2
        statusLabel.translatesAutoresizingMaskIntoConstraints = false

        let grid = NSGridView(views: [
            [baseURLLabel, baseURLField],
            [apiKeyLabel, apiKeyField],
            [modelLabel, modelField]
        ])
        grid.translatesAutoresizingMaskIntoConstraints = false
        grid.column(at: 0).xPlacement = .trailing
        grid.column(at: 1).width = 260
        grid.rowSpacing = 12
        grid.columnSpacing = 8

        let buttonStack = NSStackView(views: [testButton, saveButton])
        buttonStack.translatesAutoresizingMaskIntoConstraints = false
        buttonStack.orientation = .horizontal
        buttonStack.spacing = 12
        buttonStack.distribution = .fillEqually

        content.addSubview(grid)
        content.addSubview(buttonStack)
        content.addSubview(copyFromOtherButton)
        content.addSubview(statusLabel)

        let outputLabel: NSTextField?
        if mode == .assist {
            let label = makeLabel("输出方式:")
            outputModePopup.translatesAutoresizingMaskIntoConstraints = false
            outputModePopup.addItems(withTitles: AssistOutputMode.allCases.map { $0.displayName })
            outputModePopup.selectItem(at: Config.shared.assistOutputMode == .notch ? 1 : 0)
            outputModePopup.target = self
            outputModePopup.action = #selector(outputModeChanged)

            content.addSubview(label)
            content.addSubview(outputModePopup)

            NSLayoutConstraint.activate([
                label.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
                label.topAnchor.constraint(equalTo: grid.bottomAnchor, constant: 16),
                label.widthAnchor.constraint(equalToConstant: 90),
                outputModePopup.leadingAnchor.constraint(equalTo: label.trailingAnchor, constant: 8),
                outputModePopup.centerYAnchor.constraint(equalTo: label.centerYAnchor),
                outputModePopup.widthAnchor.constraint(equalToConstant: 160)
            ])

            // 朗读开关
            speakCheckbox.translatesAutoresizingMaskIntoConstraints = false
            speakCheckbox.state = Config.shared.assistSpeakEnabled ? .on : .off
            speakCheckbox.target = self
            speakCheckbox.action = #selector(speakToggled)

            content.addSubview(speakCheckbox)

            NSLayoutConstraint.activate([
                speakCheckbox.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
                speakCheckbox.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 10)
            ])

            outputLabel = nil
        } else {
            outputLabel = nil
        }

        let buttonTopItem: NSView
        if mode == .assist {
            buttonTopItem = speakCheckbox
        } else {
            buttonTopItem = outputLabel ?? grid
        }

        NSLayoutConstraint.activate([
            grid.topAnchor.constraint(equalTo: content.topAnchor, constant: 24),
            grid.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
            grid.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),

            buttonStack.topAnchor.constraint(equalTo: buttonTopItem.bottomAnchor, constant: 20),
            buttonStack.centerXAnchor.constraint(equalTo: content.centerXAnchor),
            buttonStack.widthAnchor.constraint(equalToConstant: 220),

            copyFromOtherButton.topAnchor.constraint(equalTo: buttonStack.bottomAnchor, constant: 10),
            copyFromOtherButton.centerXAnchor.constraint(equalTo: content.centerXAnchor),

            statusLabel.topAnchor.constraint(equalTo: copyFromOtherButton.bottomAnchor, constant: 10),
            statusLabel.centerXAnchor.constraint(equalTo: content.centerXAnchor),
            statusLabel.widthAnchor.constraint(equalToConstant: 380)
        ])
    }

    private func loadCurrentConfig() {
        let config = Config.shared.getLLMConfig(for: mode)
        baseURLField.stringValue = config.baseURL
        apiKeyField.stringValue = config.apiKey
        modelField.stringValue = config.model
        if mode == .assist {
            outputModePopup.selectItem(at: Config.shared.assistOutputMode == .notch ? 1 : 0)
            speakCheckbox.state = Config.shared.assistSpeakEnabled ? .on : .off
        }
    }

    @objc private func testConnection() {
        let url = baseURLField.stringValue.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
        let key = apiKeyField.stringValue
        let model = modelField.stringValue

        guard !url.isEmpty, !key.isEmpty, !model.isEmpty else {
            statusLabel.stringValue = "Please fill all fields"
            statusLabel.textColor = .systemRed
            return
        }

        statusLabel.stringValue = "Testing..."
        statusLabel.textColor = .systemBlue
        testButton.isEnabled = false
        saveButton.isEnabled = false

        Task { @MainActor in
            let refiner = LLMRefiner()
            let testConfig = LLMConfig(enabled: true, baseURL: url, apiKey: key, model: model)

            do {
                let result = try await refiner.testConnection(config: testConfig)
                statusLabel.stringValue = "✅ Connected! Response: \(result)"
                statusLabel.textColor = .systemGreen
            } catch {
                statusLabel.stringValue = "❌ Error: \(error.localizedDescription)"
                statusLabel.textColor = .systemRed
            }

            testButton.isEnabled = true
            saveButton.isEnabled = true
        }
    }

    @objc private func copyFromOther() {
        let otherMode: LLMMode = mode == .input ? .assist : .input
        let config = Config.shared.getLLMConfig(for: otherMode)
        baseURLField.stringValue = config.baseURL
        apiKeyField.stringValue = config.apiKey
        modelField.stringValue = config.model
        statusLabel.stringValue = "✅ 已复制「\(otherMode == .input ? "语音输入" : "AI 助手")」的配置"
        statusLabel.textColor = .systemGreen
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in
            self?.statusLabel.stringValue = ""
        }
    }

    @objc private func outputModeChanged() {
        let mode = AssistOutputMode.allCases[outputModePopup.indexOfSelectedItem]
        Config.shared.assistOutputMode = mode
        statusLabel.stringValue = "✅ 输出方式已更新"
        statusLabel.textColor = .systemGreen
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in
            self?.statusLabel.stringValue = ""
        }
    }

    @objc private func speakToggled() {
        Config.shared.assistSpeakEnabled = speakCheckbox.state == .on
        statusLabel.stringValue = speakCheckbox.state == .on ? "✅ 朗读已开启" : "✅ 朗读已关闭"
        statusLabel.textColor = .systemGreen
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in
            self?.statusLabel.stringValue = ""
        }
    }

    @objc private func saveConfig() {
        let url = baseURLField.stringValue.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
        let key = apiKeyField.stringValue
        let model = modelField.stringValue

        let config = LLMConfig(enabled: true, baseURL: url, apiKey: key, model: model)
        Config.shared.setLLMConfig(config, for: mode)

        statusLabel.stringValue = "✅ Saved"
        statusLabel.textColor = .systemGreen

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) { [weak self] in
            self?.statusLabel.stringValue = ""
        }
    }

    // MARK: - Agent UI

    override func makeKeyAndOrderFront(_ sender: Any?) {
        if mode == .agent && !agentUIBuilt {
            refreshAgentModelTable()
        }
        super.makeKeyAndOrderFront(sender)
    }

    func refreshAgentModelTable() {
        buildAgentUI()
        let modelsFile = Config.shared.loadAgentModels()
        print("[SettingsWindow] loaded models count: \(modelsFile.models.count)")
        agentModelList = modelsFile.models
        modelTableView.reloadData()

        modelPopup.removeAllItems()
        let validModels = modelsFile.models.filter {
            !$0.url.isEmpty && !$0.name.isEmpty && !$0.apikey.isEmpty && !$0.modes.isEmpty
        }
        for m in validModels {
            modelPopup.addItem(withTitle: m.name)
        }
        if let idx = validModels.firstIndex(where: { $0.name == modelsFile.defaultModel }) {
            modelPopup.selectItem(at: idx)
        } else if !validModels.isEmpty {
            modelPopup.selectItem(at: 0)
        }

        tavilyKeyField.stringValue = modelsFile.tavilyApiKey
        workspaceField.stringValue = Config.shared.agentWorkspace
        agentSpeakCheckbox.state = Config.shared.agentSpeakEnabled ? .on : .off
    }

    private func buildAgentUI() {
        guard !agentUIBuilt else { return }
        guard let content = contentView else { return }
        agentUIBuilt = true

        let modelLabel = makeLabel("默认模型:")
        modelPopup.translatesAutoresizingMaskIntoConstraints = false
        modelPopup.target = self
        modelPopup.action = #selector(agentModelChanged)

        let allowLabel = makeLabel("运行模式:")
        allowModePopup.translatesAutoresizingMaskIntoConstraints = false
        allowModePopup.addItems(withTitles: ["Read Only（只读）", "Full Access（无限制）"])
        allowModePopup.selectItem(at: Config.shared.agentAllowMode == "read_only" ? 0 : 1)
        allowModePopup.target = self
        allowModePopup.action = #selector(agentAllowModeChanged)

        let inputLabel = makeLabel("输入方式:")
        inputModePopup.translatesAutoresizingMaskIntoConstraints = false
        inputModePopup.addItems(withTitles: ["🎤 语音输入", "⌨️ 打字输入"])
        inputModePopup.selectItem(at: Config.shared.agentInputMode == "text" ? 1 : 0)
        inputModePopup.target = self
        inputModePopup.action = #selector(agentInputModeChanged)

        let thinkLabel = makeLabel("思考模式:")
        thinkingModePopup.translatesAutoresizingMaskIntoConstraints = false
        thinkingModePopup.addItems(withTitles: ["⚡ 快速", "🧠 思考"])
        thinkingModePopup.selectItem(at: Config.shared.agentThinkingMode ? 1 : 0)
        thinkingModePopup.target = self
        thinkingModePopup.action = #selector(agentThinkingModeChanged)

        agentSpeakCheckbox.translatesAutoresizingMaskIntoConstraints = false
        agentSpeakCheckbox.state = Config.shared.agentSpeakEnabled ? .on : .off
        agentSpeakCheckbox.target = self
        agentSpeakCheckbox.action = #selector(agentSpeakToggled)

        let tavilyLabel = makeLabel("Tavily Key:")
        tavilyKeyField.placeholderString = "tvly-...（网络搜索用）"
        tavilyKeyField.font = .systemFont(ofSize: 13)
        tavilyKeyField.bezelStyle = .roundedBezel
        tavilyKeyField.translatesAutoresizingMaskIntoConstraints = false

        let wsLabel = makeLabel("工作目录:")
        workspaceField.placeholderString = NSHomeDirectory()
        workspaceField.font = .systemFont(ofSize: 12)
        workspaceField.bezelStyle = .roundedBezel
        workspaceField.translatesAutoresizingMaskIntoConstraints = false

        browseButton.bezelStyle = .rounded
        browseButton.translatesAutoresizingMaskIntoConstraints = false
        browseButton.target = self
        browseButton.action = #selector(browseWorkspace)

        let tableHeaderLabel = NSTextField(labelWithString: "模型列表")
        tableHeaderLabel.font = .systemFont(ofSize: 12, weight: .semibold)
        tableHeaderLabel.translatesAutoresizingMaskIntoConstraints = false

        addModelButton.bezelStyle = .rounded
        addModelButton.font = .systemFont(ofSize: 13, weight: .bold)
        addModelButton.translatesAutoresizingMaskIntoConstraints = false
        addModelButton.target = self
        addModelButton.action = #selector(addAgentModel)

        let scrollView = NSScrollView(frame: .zero)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.hasVerticalScroller = true
        scrollView.borderType = .bezelBorder

        modelTableView.translatesAutoresizingMaskIntoConstraints = false
        modelTableView.headerView = nil
        modelTableView.selectionHighlightStyle = .regular
        modelTableView.dataSource = self
        modelTableView.delegate = self
        modelTableView.rowHeight = 24
        modelTableView.intercellSpacing = NSSize(width: 4, height: 2)

        let colName = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("name"))
        colName.title = "模型名称"; colName.width = 120; colName.minWidth = 80
        let colURL = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("url"))
        colURL.title = "API URL"; colURL.width = 150; colURL.minWidth = 100
        let colKey = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("key"))
        colKey.title = "API Key"; colKey.width = 90; colKey.minWidth = 70
        let colModes = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("modes"))
        colModes.title = "模式"; colModes.width = 70; colModes.minWidth = 50

        modelTableView.addTableColumn(colName)
        modelTableView.addTableColumn(colURL)
        modelTableView.addTableColumn(colKey)
        modelTableView.addTableColumn(colModes)

        scrollView.documentView = modelTableView

        editModelButton.bezelStyle = .rounded
        editModelButton.translatesAutoresizingMaskIntoConstraints = false
        editModelButton.target = self
        editModelButton.action = #selector(editAgentModel)

        deleteModelButton.bezelStyle = .rounded
        deleteModelButton.translatesAutoresizingMaskIntoConstraints = false
        deleteModelButton.target = self
        deleteModelButton.action = #selector(deleteAgentModel)

        let editDeleteStack = NSStackView(views: [editModelButton, deleteModelButton])
        editDeleteStack.translatesAutoresizingMaskIntoConstraints = false
        editDeleteStack.orientation = .horizontal
        editDeleteStack.spacing = 8

        saveButton.bezelStyle = .rounded
        saveButton.translatesAutoresizingMaskIntoConstraints = false
        saveButton.keyEquivalent = "\r"
        saveButton.target = self
        saveButton.action = #selector(saveAgentConfig)

        statusLabel.font = .systemFont(ofSize: 11)
        statusLabel.textColor = .secondaryLabelColor
        statusLabel.alignment = .center
        statusLabel.maximumNumberOfLines = 2
        statusLabel.translatesAutoresizingMaskIntoConstraints = false

        content.addSubview(modelLabel)
        content.addSubview(modelPopup)
        content.addSubview(allowLabel)
        content.addSubview(allowModePopup)
        content.addSubview(inputLabel)
        content.addSubview(inputModePopup)
        content.addSubview(thinkLabel)
        content.addSubview(thinkingModePopup)
        content.addSubview(agentSpeakCheckbox)
        content.addSubview(tavilyLabel)
        content.addSubview(tavilyKeyField)
        content.addSubview(wsLabel)
        content.addSubview(workspaceField)
        content.addSubview(browseButton)
        content.addSubview(tableHeaderLabel)
        content.addSubview(addModelButton)
        content.addSubview(scrollView)
        content.addSubview(editDeleteStack)
        content.addSubview(saveButton)
        content.addSubview(statusLabel)

        NSLayoutConstraint.activate([
            modelLabel.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
            modelLabel.topAnchor.constraint(equalTo: content.topAnchor, constant: 24),
            modelLabel.widthAnchor.constraint(equalToConstant: 85),
            modelPopup.leadingAnchor.constraint(equalTo: modelLabel.trailingAnchor, constant: 8),
            modelPopup.centerYAnchor.constraint(equalTo: modelLabel.centerYAnchor),
            modelPopup.widthAnchor.constraint(equalToConstant: 200),

            allowLabel.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
            allowLabel.topAnchor.constraint(equalTo: modelLabel.bottomAnchor, constant: 16),
            allowLabel.widthAnchor.constraint(equalToConstant: 85),
            allowModePopup.leadingAnchor.constraint(equalTo: allowLabel.trailingAnchor, constant: 8),
            allowModePopup.centerYAnchor.constraint(equalTo: allowLabel.centerYAnchor),
            allowModePopup.widthAnchor.constraint(equalToConstant: 200),

            inputLabel.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
            inputLabel.topAnchor.constraint(equalTo: allowLabel.bottomAnchor, constant: 16),
            inputLabel.widthAnchor.constraint(equalToConstant: 85),
            inputModePopup.leadingAnchor.constraint(equalTo: inputLabel.trailingAnchor, constant: 8),
            inputModePopup.centerYAnchor.constraint(equalTo: inputLabel.centerYAnchor),
            inputModePopup.widthAnchor.constraint(equalToConstant: 200),

            thinkLabel.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
            thinkLabel.topAnchor.constraint(equalTo: inputLabel.bottomAnchor, constant: 16),
            thinkLabel.widthAnchor.constraint(equalToConstant: 85),
            thinkingModePopup.leadingAnchor.constraint(equalTo: thinkLabel.trailingAnchor, constant: 8),
            thinkingModePopup.centerYAnchor.constraint(equalTo: thinkLabel.centerYAnchor),
            thinkingModePopup.widthAnchor.constraint(equalToConstant: 200),

            agentSpeakCheckbox.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
            agentSpeakCheckbox.topAnchor.constraint(equalTo: thinkLabel.bottomAnchor, constant: 10),

            tavilyLabel.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
            tavilyLabel.topAnchor.constraint(equalTo: agentSpeakCheckbox.bottomAnchor, constant: 14),
            tavilyLabel.widthAnchor.constraint(equalToConstant: 85),
            tavilyKeyField.leadingAnchor.constraint(equalTo: tavilyLabel.trailingAnchor, constant: 8),
            tavilyKeyField.centerYAnchor.constraint(equalTo: tavilyLabel.centerYAnchor),
            tavilyKeyField.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),

            wsLabel.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
            wsLabel.topAnchor.constraint(equalTo: tavilyLabel.bottomAnchor, constant: 16),
            wsLabel.widthAnchor.constraint(equalToConstant: 85),
            workspaceField.leadingAnchor.constraint(equalTo: wsLabel.trailingAnchor, constant: 8),
            workspaceField.centerYAnchor.constraint(equalTo: wsLabel.centerYAnchor),
            workspaceField.trailingAnchor.constraint(equalTo: browseButton.leadingAnchor, constant: -6),
            browseButton.centerYAnchor.constraint(equalTo: wsLabel.centerYAnchor),
            browseButton.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),
            browseButton.widthAnchor.constraint(equalToConstant: 60),

            tableHeaderLabel.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 22),
            tableHeaderLabel.topAnchor.constraint(equalTo: wsLabel.bottomAnchor, constant: 22),
            addModelButton.centerYAnchor.constraint(equalTo: tableHeaderLabel.centerYAnchor),
            addModelButton.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),
            addModelButton.widthAnchor.constraint(equalToConstant: 28),

            scrollView.topAnchor.constraint(equalTo: tableHeaderLabel.bottomAnchor, constant: 6),
            scrollView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 20),
            scrollView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -20),
            scrollView.heightAnchor.constraint(equalToConstant: 170),

            editDeleteStack.topAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 8),
            editDeleteStack.centerXAnchor.constraint(equalTo: content.centerXAnchor),

            saveButton.topAnchor.constraint(equalTo: editDeleteStack.bottomAnchor, constant: 12),
            saveButton.centerXAnchor.constraint(equalTo: content.centerXAnchor),
            saveButton.widthAnchor.constraint(equalToConstant: 140),

            statusLabel.topAnchor.constraint(equalTo: saveButton.bottomAnchor, constant: 8),
            statusLabel.centerXAnchor.constraint(equalTo: content.centerXAnchor),
            statusLabel.widthAnchor.constraint(equalToConstant: 460)
        ])

        refreshAgentModelTable()
    }

    @objc private func browseWorkspace() {
        let panel = NSOpenPanel()
        panel.canChooseDirectories = true
        panel.canChooseFiles = false
        panel.message = "选择智能体工作目录"
        panel.begin { [weak self] response in
            guard response == .OK, let url = panel.url else { return }
            self?.workspaceField.stringValue = url.path
            Config.shared.agentWorkspace = url.path
        }
    }

    // MARK: - Agent 事件

    @objc private func agentModelChanged() {}
    @objc private func agentAllowModeChanged() {
        Config.shared.agentAllowMode = allowModePopup.indexOfSelectedItem == 0 ? "read_only" : "full_access"
    }
    @objc private func agentInputModeChanged() {
        Config.shared.agentInputMode = inputModePopup.indexOfSelectedItem == 0 ? "voice" : "text"
    }
    @objc private func agentThinkingModeChanged() {
        Config.shared.agentThinkingMode = thinkingModePopup.indexOfSelectedItem == 1
    }

    @objc private func agentSpeakToggled() {
        Config.shared.agentSpeakEnabled = agentSpeakCheckbox.state == .on
        statusLabel.stringValue = agentSpeakCheckbox.state == .on ? "✅ 朗读已开启" : "✅ 朗读已关闭"
        statusLabel.textColor = .systemGreen
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in
            self?.statusLabel.stringValue = ""
        }
    }

    @objc private func saveAgentConfig() {
        var modelsFile = Config.shared.loadAgentModels()
        modelsFile.tavilyApiKey = tavilyKeyField.stringValue.trimmingCharacters(in: .whitespaces)
        modelsFile.defaultModel = modelPopup.titleOfSelectedItem ?? ""
        modelsFile.models = agentModelList
        Config.shared.saveAgentModels(modelsFile)
        Config.shared.agentWorkspace = workspaceField.stringValue
        refreshAgentModelTable()
        statusLabel.stringValue = "✅ Agent 配置已保存"
        statusLabel.textColor = .systemGreen
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in
            self?.statusLabel.stringValue = ""
        }
    }

    @objc private func addAgentModel() {
        AgentModelEditor.show(for: nil) { [weak self] newModel in
            guard let self = self else { return }
            self.agentModelList.append(newModel)
            self.modelTableView.reloadData()
            self.statusLabel.stringValue = "✅ 模型已添加（点击 Save 保存到文件）"
            self.statusLabel.textColor = .systemGreen
        }
    }

    @objc private func editAgentModel() {
        let selected = modelTableView.selectedRow
        guard selected >= 0, selected < agentModelList.count else {
            statusLabel.stringValue = "请先选中一个模型"
            statusLabel.textColor = .systemRed
            return
        }
        let original = agentModelList[selected]
        AgentModelEditor.show(for: original) { [weak self] newModel in
            guard let self = self else { return }
            if let idx = self.agentModelList.firstIndex(where: { $0.name == original.name }) {
                self.agentModelList[idx] = newModel
            }
            self.modelTableView.reloadData()
            self.statusLabel.stringValue = "✅ 模型已更新（点击 Save 保存到文件）"
            self.statusLabel.textColor = .systemGreen
        }
    }

    @objc private func deleteAgentModel() {
        let selected = modelTableView.selectedRow
        guard selected >= 0, selected < agentModelList.count else {
            statusLabel.stringValue = "请先选中一个模型"
            statusLabel.textColor = .systemRed
            return
        }
        let model = agentModelList[selected]
        let alert = NSAlert()
        alert.messageText = "删除模型"
        alert.informativeText = "确定要删除模型「\(model.name)」吗？"
        alert.alertStyle = .warning
        alert.addButton(withTitle: "删除")
        alert.addButton(withTitle: "取消")
        if alert.runModal() == .alertFirstButtonReturn {
            agentModelList.remove(at: selected)
            modelTableView.reloadData()
            statusLabel.stringValue = "✅ 已删除（点击 Save 保存到文件）"
            statusLabel.textColor = .systemGreen
        }
    }

}

// MARK: - NSTableView

extension SettingsWindow: NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int { agentModelList.count }
}

extension SettingsWindow: NSTableViewDelegate {
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        guard row < agentModelList.count, let column = tableColumn else { return nil }
        let model = agentModelList[row]
        let textField: NSTextField
        if let reused = tableView.makeView(withIdentifier: column.identifier, owner: nil) as? NSTextField {
            textField = reused
        } else {
            textField = NSTextField(labelWithString: "")
            textField.identifier = column.identifier
            textField.font = .systemFont(ofSize: 11)
            textField.lineBreakMode = .byTruncatingTail
            textField.maximumNumberOfLines = 1
        }
        switch column.identifier.rawValue {
        case "name":  textField.stringValue = model.name
        case "url":   textField.stringValue = model.url
        case "key":   textField.stringValue = model.maskedKey
        case "modes": textField.stringValue = model.modes
        default:      textField.stringValue = ""
        }
        return textField
    }
}
