import Foundation

struct AiPrompts: Codable, Equatable {
    var pdfAsk: String
    var videoAsk: String
    var audioAsk: String
    var imageRegion: String
    var voiceCall: String
    var screenShare: String

    enum CodingKeys: String, CodingKey {
        case pdfAsk, videoAsk, audioAsk, imageRegion, voiceCall, screenShare
    }

    init(
        pdfAsk: String,
        videoAsk: String,
        audioAsk: String,
        imageRegion: String = "",
        voiceCall: String = "",
        screenShare: String = ""
    ) {
        self.pdfAsk = pdfAsk
        self.videoAsk = videoAsk
        self.audioAsk = audioAsk
        self.imageRegion = imageRegion
        self.voiceCall = voiceCall
        self.screenShare = screenShare
    }

    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        pdfAsk = try c.decode(String.self, forKey: .pdfAsk)
        videoAsk = try c.decode(String.self, forKey: .videoAsk)
        audioAsk = try c.decode(String.self, forKey: .audioAsk)
        imageRegion = try c.decodeIfPresent(String.self, forKey: .imageRegion) ?? ""
        voiceCall = try c.decodeIfPresent(String.self, forKey: .voiceCall) ?? ""
        screenShare = try c.decodeIfPresent(String.self, forKey: .screenShare) ?? ""
    }
}

struct AiFeatures: Codable, Equatable {
    var pdfChat: Bool
    var videoRegionAsk: Bool
    var audioClipAsk: Bool
    var voiceMessage: Bool
    var realtimeVoiceCall: Bool
    var realtimeScreenShare: Bool

    enum CodingKeys: String, CodingKey {
        case pdfChat, videoRegionAsk, audioClipAsk
        case voiceMessage, realtimeVoiceCall, realtimeScreenShare
    }

    init(
        pdfChat: Bool,
        videoRegionAsk: Bool,
        audioClipAsk: Bool,
        voiceMessage: Bool = true,
        realtimeVoiceCall: Bool = true,
        realtimeScreenShare: Bool = true
    ) {
        self.pdfChat = pdfChat
        self.videoRegionAsk = videoRegionAsk
        self.audioClipAsk = audioClipAsk
        self.voiceMessage = voiceMessage
        self.realtimeVoiceCall = realtimeVoiceCall
        self.realtimeScreenShare = realtimeScreenShare
    }

    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        pdfChat = try c.decode(Bool.self, forKey: .pdfChat)
        videoRegionAsk = try c.decode(Bool.self, forKey: .videoRegionAsk)
        audioClipAsk = try c.decode(Bool.self, forKey: .audioClipAsk)
        voiceMessage = try c.decodeIfPresent(Bool.self, forKey: .voiceMessage) ?? true
        realtimeVoiceCall = try c.decodeIfPresent(Bool.self, forKey: .realtimeVoiceCall) ?? true
        realtimeScreenShare = try c.decodeIfPresent(Bool.self, forKey: .realtimeScreenShare) ?? true
    }
}

struct AiEndpoint: Codable, Equatable, Identifiable {
    var id: String
    var capability: String
    var provider: String
    var model: String
    var baseUrl: String
    var enabled: Bool
    var apiKey: String

    enum CodingKeys: String, CodingKey {
        case id, capability, provider, model, baseUrl, enabled, apiKey
    }

    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        id = try c.decode(String.self, forKey: .id)
        capability = try c.decode(String.self, forKey: .capability)
        provider = try c.decode(String.self, forKey: .provider)
        model = try c.decode(String.self, forKey: .model)
        baseUrl = try c.decode(String.self, forKey: .baseUrl)
        enabled = try c.decodeIfPresent(Bool.self, forKey: .enabled) ?? true
        apiKey = try c.decodeIfPresent(String.self, forKey: .apiKey) ?? ""
    }
}

struct AiRuntimeConfig: Codable, Equatable {
    var version: UInt64
    var updatedAt: String
    var source: String
    var profile: String
    var vendor: String
    var model: String
    var baseUrl: String
    var apiKey: String
    var endpoints: [AiEndpoint]
    var prompts: AiPrompts
    var features: AiFeatures
    var rtcAppId: String
    var rtcTokenUrl: String
    var serverSyncEnabled: Bool
    var serverBaseUrl: String

    enum CodingKeys: String, CodingKey {
        case version, updatedAt, source, profile, vendor, model, baseUrl, apiKey
        case endpoints, prompts, features, rtcAppId, rtcTokenUrl
        case serverSyncEnabled, serverBaseUrl
    }

    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        version = try c.decode(UInt64.self, forKey: .version)
        updatedAt = try c.decode(String.self, forKey: .updatedAt)
        source = try c.decode(String.self, forKey: .source)
        profile = try c.decodeIfPresent(String.self, forKey: .profile) ?? "openai"
        vendor = try c.decode(String.self, forKey: .vendor)
        model = try c.decode(String.self, forKey: .model)
        baseUrl = try c.decode(String.self, forKey: .baseUrl)
        apiKey = try c.decode(String.self, forKey: .apiKey)
        endpoints = try c.decodeIfPresent([AiEndpoint].self, forKey: .endpoints) ?? []
        prompts = try c.decode(AiPrompts.self, forKey: .prompts)
        features = try c.decode(AiFeatures.self, forKey: .features)
        rtcAppId = try c.decodeIfPresent(String.self, forKey: .rtcAppId) ?? ""
        rtcTokenUrl = try c.decodeIfPresent(String.self, forKey: .rtcTokenUrl) ?? ""
        serverSyncEnabled = try c.decodeIfPresent(Bool.self, forKey: .serverSyncEnabled) ?? false
        serverBaseUrl = try c.decodeIfPresent(String.self, forKey: .serverBaseUrl) ?? ""
    }

    func endpoint(for capability: String) -> AiEndpoint? {
        if let ep = endpoints.first(where: { $0.capability == capability && $0.enabled }) {
            return ep
        }
        if capability == "vision" {
            return endpoints.first(where: { $0.capability == "chat" && $0.enabled })
        }
        // Legacy single-model fallback
        if !apiKey.isEmpty {
            return AiEndpoint(
                id: "legacy",
                capability: capability,
                provider: vendor,
                model: model,
                baseUrl: baseUrl,
                enabled: true,
                apiKey: apiKey
            )
        }
        return nil
    }
}

// Manual memberwise for legacy fallback construction
extension AiEndpoint {
    init(
        id: String,
        capability: String,
        provider: String,
        model: String,
        baseUrl: String,
        enabled: Bool,
        apiKey: String
    ) {
        self.id = id
        self.capability = capability
        self.provider = provider
        self.model = model
        self.baseUrl = baseUrl
        self.enabled = enabled
        self.apiKey = apiKey
    }
}

enum AiConfigResolveError: LocalizedError {
    case unpaired
    case shareUnavailable
    case noCache
    case stale
    case featureDisabled

    var errorDescription: String? {
        switch self {
        case .unpaired: return "尚未与电脑配对 AI 配置。请在菜单中扫码配对。"
        case .shareUnavailable: return "电脑 AI 分享未开启或不可达，且本机无历史配置。"
        case .noCache: return "离线不可用：没有可用的历史 AI 配置。"
        case .stale: return "配置已失效，请重新与电脑配对同步。"
        case .featureDisabled: return "该 AI 功能未开启。"
        }
    }
}
