import Foundation

/// 通过 OpenAI 兼容 API 进行 LLM 处理
final class LLMRefiner {

    private let session: URLSession = {
        let config = URLSessionConfiguration.default
        config.timeoutIntervalForRequest = 30
        config.timeoutIntervalForResource = 60
        return URLSession(configuration: config)
    }()

    struct RefinementResult {
        let text: String
        let wasChanged: Bool
    }

    // MARK: - Fn 键模式：语音输入纠错

    func refine(text: String, config: LLMConfig) async throws -> RefinementResult {
        let systemPrompt = """
        你是一个语音识别后处理工具。你唯一的任务是修正转录文本中的明显识别错误。规则：
        1. 修正中文同音字/词错误。语音识别经常把技术词汇识别为同音的日常词汇，例如：
           「变异」→「编译」、「程序圆」→「程序员」、「配森」→「Python」。
           请根据上下文判断修正方向，将同音错误还原为正确的词汇。
        2. 修正被错误转录为中文的英文技术术语。
        3. 修正极其明显的中文和英文拼写错误（包括错别字、漏字、多字）。
        4. 修正明显的乱码或无意义字符序列。
        5. 绝不重写、润色、改进或修改任何看起来正确的内容。
        6. 如果输入本身正确，请原样返回，不做任何修改。
        7. 保留所有标点符号、空格、大小写和格式。
        8. 只返回修正后的文本，不要添加任何解释、前缀、后缀或 Markdown 标记。
        """
        
        return try await callLLM(
            text: text,
            systemPrompt: systemPrompt,
            config: config
        )
    }

    // MARK: - Control 键模式：AI 助手对话

    func chat(text: String, config: LLMConfig) async throws -> RefinementResult {
        let systemPrompt = """
        你是一个 AI 助手，用户通过语音输入与你交流。用户的输入来自语音识别，可能存在同音字或转录错误，请结合上下文理解。

        你的任务：
        1. 回答用户的问题、提供信息或执行对话
        2. 保持回答简洁自然，适合口语化场景
        3. 如果用户输入模糊，尝试猜测意图并给出最可能的回答
        4. 使用用户正在使用的语言，或用户明确要求的语言进行回复
        5. 不要包含任何解释性文字、前缀、后缀或 Markdown 标记
        6. 你的输出将直接进入用户的输入框，所以只返回最终回答内容
        """
        
        return try await callLLM(
            text: text,
            systemPrompt: systemPrompt,
            config: config
        )
    }

    // MARK: - 通用调用

    private func callLLM(text: String, systemPrompt: String, config: LLMConfig) async throws -> RefinementResult {
        let baseURL = config.baseURL.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
        let url = URL(string: "\(baseURL)/v1/chat/completions")!

        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = "POST"
        urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        urlRequest.setValue("Bearer \(config.apiKey)", forHTTPHeaderField: "Authorization")

        let messages: [[String: String]] = [
            ["role": "system", "content": systemPrompt],
            ["role": "user", "content": text]
        ]

        let body: [String: Any] = [
            "model": config.model,
            "messages": messages,
            "max_tokens": 4096,
            "thinking": ["type": "disabled"]
        ]

        urlRequest.httpBody = try JSONSerialization.data(withJSONObject: body)

        let (data, response) = try await session.data(for: urlRequest)

        guard let httpResponse = response as? HTTPURLResponse else {
            throw NSError(domain: "LLMRefiner", code: -1,
                          userInfo: [NSLocalizedDescriptionKey: "Invalid response"])
        }

        guard httpResponse.statusCode == 200 else {
            let body = String(data: data, encoding: .utf8) ?? "Unknown"
            throw NSError(domain: "LLMRefiner", code: httpResponse.statusCode,
                          userInfo: [NSLocalizedDescriptionKey: "HTTP \(httpResponse.statusCode): \(body)"])
        }

        guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
              let choices = json["choices"] as? [[String: Any]],
              let firstChoice = choices.first,
              let message = firstChoice["message"] as? [String: Any],
              let result = message["content"] as? String else {
            throw NSError(domain: "LLMRefiner", code: -2,
                          userInfo: [NSLocalizedDescriptionKey: "Failed to parse response"])
        }

        let trimmed = result.trimmingCharacters(in: .whitespacesAndNewlines)
        let wasChanged = trimmed != text.trimmingCharacters(in: .whitespacesAndNewlines)

        return RefinementResult(text: trimmed, wasChanged: wasChanged)
    }

    /// 测试连接
    func testConnection(config: LLMConfig) async throws -> String {
        let testText = "测试连接"
        let result = try await callLLM(
            text: testText,
            systemPrompt: "You are a helpful assistant. Reply with 'OK' only.",
            config: config
        )
        return result.text
    }
}
