import Foundation

public enum TokenUsageParser {
    public static func parseClaudeLine(_ line: String, sourcePath: String) -> UsageRecord? {
        guard let object = parseJSONObject(line) else { return nil }
        let message = object["message"] as? [String: Any]
        let usage = message?["usage"] as? [String: Any] ?? object["usage"] as? [String: Any]
        guard let usage else { return nil }
        guard let timestamp = parseDate(object["timestamp"] ?? object["createdAt"] ?? object["date"]) else { return nil }

        return UsageRecord(
            provider: .claude,
            source: sourceLabel(sourcePath: sourcePath, provider: .claude),
            sourcePath: sourcePath,
            timestamp: timestamp,
            sessionID: stringValue(object["sessionId"]),
            model: stringValue(message?["model"]) == "" ? "Claude" : stringValue(message?["model"]),
            inputTokens: intValue(usage["input_tokens"]),
            outputTokens: intValue(usage["output_tokens"]),
            cachedTokens: intValue(usage["cache_read_input_tokens"]) + intValue(usage["cache_creation_input_tokens"]),
            reasoningTokens: 0,
            toolTokens: 0
        )
    }

    public static func parseCodexLine(_ line: String, sourcePath: String) -> UsageRecord? {
        guard let object = parseJSONObject(line),
              let payload = object["payload"] as? [String: Any],
              let info = payload["info"] as? [String: Any],
              let usage = info["last_token_usage"] as? [String: Any],
              let timestamp = parseDate(object["timestamp"])
        else { return nil }

        return UsageRecord(
            provider: .codex,
            source: sourceLabel(sourcePath: sourcePath, provider: .codex),
            sourcePath: sourcePath,
            timestamp: timestamp,
            sessionID: stringValue(payload["id"] ?? payload["thread_id"]),
            model: stringValue(payload["model"]) == "" ? "Codex" : stringValue(payload["model"]),
            inputTokens: intValue(usage["input_tokens"]),
            outputTokens: intValue(usage["output_tokens"]),
            cachedTokens: intValue(usage["cached_input_tokens"]),
            reasoningTokens: intValue(usage["reasoning_output_tokens"]),
            toolTokens: 0
        )
    }

    public static func parseGeminiLine(_ line: String, sourcePath: String) -> UsageRecord? {
        guard let object = parseJSONObject(line),
              let tokens = object["tokens"] as? [String: Any],
              let timestamp = parseDate(object["timestamp"])
        else { return nil }

        return UsageRecord(
            provider: .gemini,
            source: sourceLabel(sourcePath: sourcePath, provider: .gemini),
            sourcePath: sourcePath,
            timestamp: timestamp,
            sessionID: stringValue(object["id"]),
            model: stringValue(object["model"]) == "" ? "Gemini" : stringValue(object["model"]),
            inputTokens: intValue(tokens["input"]),
            outputTokens: intValue(tokens["output"]),
            cachedTokens: intValue(tokens["cached"]),
            reasoningTokens: intValue(tokens["thoughts"]),
            toolTokens: intValue(tokens["tool"]),
            explicitTotal: intValue(tokens["total"]) == 0 ? nil : intValue(tokens["total"])
        )
    }

    static func parseDate(_ value: Any?) -> Date? {
        guard let text = value as? String else { return nil }
        let isoFormatter = ISO8601DateFormatter()
        isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        let fallbackISOFormatter = ISO8601DateFormatter()
        fallbackISOFormatter.formatOptions = [.withInternetDateTime]
        return isoFormatter.date(from: text) ?? fallbackISOFormatter.date(from: text)
    }

    private static func parseJSONObject(_ line: String) -> [String: Any]? {
        guard let data = line.data(using: .utf8) else { return nil }
        return (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
    }

    private static func stringValue(_ value: Any?) -> String {
        value as? String ?? ""
    }

    private static func intValue(_ value: Any?) -> Int {
        if let int = value as? Int { return int }
        if let double = value as? Double { return Int(double) }
        if let string = value as? String { return Int(string) ?? 0 }
        return 0
    }

    private static func sourceLabel(sourcePath: String, provider: Provider) -> String {
        switch provider {
        case .claude: return "projects/*.jsonl"
        case .codex: return "sessions/*.jsonl"
        case .gemini: return "tmp/chats/*.jsonl"
        case .hermes: return "state.db/sessions"
        case .opencode: return "opencode.db/session"
        }
    }
}
