import Foundation

// MARK: - Agent 事件类型

enum AgentEvent {
    case status(mode: String)
    case toolStart(tool: String, display: String)
    case toolEnd(tool: String, resultLines: [String])
    case assistantChunk(content: String)
    case done(content: String, elapsedMs: Int, totalTokens: Int)
    case error(message: String)
}

// MARK: - AgentBridge

final class AgentBridge {

    private var process: Process?
    private var stdin: Pipe?
    private let agentDir: String
    private let logPath: String
    private var eventSeq = 0

    var onEvent: ((AgentEvent) -> Void)?
    var onExit: ((Int32) -> Void)?

    init(agentDir: String) {
        self.agentDir = agentDir
        // 日志写到用户目录（app bundle 内无写权限）
        let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
        let logDir = appSupport.appendingPathComponent("VoiceInput").path
        if !FileManager.default.fileExists(atPath: logDir) {
            try? FileManager.default.createDirectory(atPath: logDir, withIntermediateDirectories: true)
        }
        self.logPath = logDir + "/agent_debug.log"
        // 清空旧日志
        try? "".write(toFile: logPath, atomically: true, encoding: .utf8)
        log("======== AgentBridge 日志开始 ========")
        log("agentDir: \(agentDir)")
    }

    private func log(_ msg: String) {
        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()
        } else {
            try? line.write(toFile: logPath, atomically: true, encoding: .utf8)
        }
    }

    /// 启动 Node.js 桥接进程
    func start() -> Bool {
        guard let nodePath = findNode() else {
            print("[AgentBridge] ❌ Node.js not found")
            return false
        }

        let bridgePath = "\(agentDir)/src/bridge.js"
        guard FileManager.default.fileExists(atPath: bridgePath) else {
            print("[AgentBridge] ❌ bridge.js not found at \(bridgePath)")
            return false
        }

        let process = Process()
        process.executableURL = URL(fileURLWithPath: nodePath)
        process.arguments = [bridgePath, "--config", Config.shared.userAgentModelsPath]
        process.currentDirectoryURL = URL(fileURLWithPath: agentDir)

        // stdin → 向 bridge 发送输入
        let stdinPipe = Pipe()
        process.standardInput = stdinPipe
        self.stdin = stdinPipe

        // stdout → 读取 JSON 事件
        let stdoutPipe = Pipe()
        process.standardOutput = stdoutPipe

        // stderr → 只打印日志
        let stderrPipe = Pipe()
        process.standardError = stderrPipe

        // 读取 stdout
        stdoutPipe.fileHandleForReading.readabilityHandler = { [weak self] handle in
            let data = handle.availableData
            guard !data.isEmpty, let self = self else { return }
            if let str = String(data: data, encoding: .utf8) {
                let lines = str.components(separatedBy: "\n").filter { !$0.isEmpty }
                for line in lines {
                    self.parseLine(line)
                }
            }
        }

        // 读取 stderr
        stderrPipe.fileHandleForReading.readabilityHandler = { [weak self] handle in
            let data = handle.availableData
            if !data.isEmpty, let str = String(data: data, encoding: .utf8) {
                if !str.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                    let trimmed = str.trimmingCharacters(in: .newlines)
                    print("[Agent] \(trimmed)")
                    self?.log("stderr: \(trimmed)")
                }
            }
        }

        process.terminationHandler = { [weak self] proc in
            let code = proc.terminationStatus
            let reason = proc.terminationReason
            self?.log("进程退出: status=\(code), reason=\(reason.rawValue)")
            self?.onExit?(code)
        }

        do {
            try process.run()
            self.process = process
            print("[AgentBridge] ✅ Started (pid \(process.processIdentifier))")
            return true
        } catch {
            print("[AgentBridge] ❌ Failed to start: \(error)")
            return false
        }
    }

    /// 发送对话请求（第一轮）
    func send(message: String, workspace: String, allowMode: String = "full_access", modelKey: String? = nil, newConversation: Bool = false) {
        sendInternal(message: message, workspace: workspace, allowMode: allowMode, modelKey: modelKey, newConversation: newConversation)
    }

    /// 追加消息（同一对话）
    func sendContinue(message: String) {
        sendInternal(message: message, workspace: "", allowMode: "", modelKey: nil)
    }

    private func sendInternal(message: String, workspace: String, allowMode: String, modelKey: String?, newConversation: Bool = false) {
        var payload: [String: Any] = [
            "action": "chat",
            "message": message,
        ]
        if !workspace.isEmpty { payload["workspace"] = workspace }
        if !allowMode.isEmpty { payload["allowMode"] = allowMode }
        if let mk = modelKey, !mk.isEmpty { payload["modelKey"] = mk }
        payload["thinkingMode"] = Config.shared.agentThinkingMode
        if newConversation { payload["new_conversation"] = true }

        guard let data = try? JSONSerialization.data(withJSONObject: payload),
              let json = String(data: data, encoding: .utf8) else {
            log("send() ❌ JSON 编码失败")
            print("[AgentBridge] ❌ Failed to encode message")
            return
        }
        log("send() JSON: \(json.prefix(200))")

        if let handle = stdin?.fileHandleForWriting {
            handle.write((json + "\n").data(using: .utf8)!)
            log("send() ✅ 已写入 stdin")
        } else {
            log("send() ❌ stdin handle 为 nil")
        }
    }

    /// 停止进程
    func stop() {
        log("stop() 终止进程")
        process?.terminate()
        process = nil
    }

    // MARK: - Private

    private func findNode() -> String? {
        // 优先级 0：bundle 内打包的 Node.js
        let bundledNode = agentDir + "/bin/node"
        if FileManager.default.fileExists(atPath: bundledNode) {
            return bundledNode
        }
        // 回退到系统路径
        let paths = [
            "/opt/homebrew/bin/node",
            "/usr/local/bin/node",
            "/usr/bin/node"
        ]
        for p in paths where FileManager.default.fileExists(atPath: p) {
            return p
        }
        // 尝试 which
        let task = Process()
        task.executableURL = URL(fileURLWithPath: "/usr/bin/env")
        task.arguments = ["which", "node"]
        let pipe = Pipe()
        task.standardOutput = pipe
        do {
            try task.run()
            task.waitUntilExit()
            let data = pipe.fileHandleForReading.readDataToEndOfFile()
            if let path = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines),
               !path.isEmpty {
                return path
            }
        } catch {
            print("[AgentBridge] which node failed: \(error)")
        }
        return nil
    }

    private func parseLine(_ line: String) {
        eventSeq += 1
        guard let data = line.data(using: .utf8),
              let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
              let type = json["type"] as? String else {
            log("parseLine #\(eventSeq) 解析失败: \(line.prefix(100))")
            return
        }
        log("parseLine #\(eventSeq) type=\(type)")

        switch type {
        case "status":
            let mode = json["mode"] as? String ?? "unknown"
            log("  → status mode=\(mode)")
            onEvent?(.status(mode: mode))
        case "tool_start":
            let tool = json["tool"] as? String ?? "unknown"
            log("  → tool_start tool=\(tool)")
            onEvent?(.toolStart(
                tool: tool,
                display: json["display"] as? String ?? ""
            ))
        case "tool_end":
            let tool = json["tool"] as? String ?? "unknown"
            let resultLines = json["result"] as? [String] ?? []
            log("  → tool_end tool=\(tool) resultLines=\(resultLines.count)")
            onEvent?(.toolEnd(tool: tool, resultLines: resultLines))
        case "assistant_chunk":
            let content = json["content"] as? String ?? ""
            log("  → assistant_chunk len=\(content.count)")
            onEvent?(.assistantChunk(content: content))
        case "done":
            let content = json["content"] as? String ?? ""
            let elapsed = json["elapsed_ms"] as? Int ?? 0
            let totalTokens = json["total_tokens"] as? Int ?? 0
            log("  → done contentLen=\(content.count) elapsedMs=\(elapsed) totalTokens=\(totalTokens)")
            onEvent?(.done(content: content, elapsedMs: elapsed, totalTokens: totalTokens))
        case "error":
            let message = json["message"] as? String ?? "未知错误"
            log("  → error message=\(message)")
            onEvent?(.error(message: message))
        default:
            log("  → 未知 type: \(type)")
            break
        }
    }
}
