import Foundation

/**
 * Top-level application configuration returned from the BigCrunch backend
 *
 * This config contains all placement definitions and is fetched once at SDK initialization
 * and cached for subsequent sessions.
 */
public struct AppConfig: Codable {
    public let propertyId: String
    public let appName: String
    public let gamNetworkCode: String
    public let s2s: S2SConfig
    public let bidders: [String: BidderEntry]?
    public let amazonAps: GlobalAmazonConfig?
    public let useTestAds: Bool
    public let refresh: RefreshConfig?
    public let placements: [PlacementConfig]

    public init(
        propertyId: String,
        appName: String,
        gamNetworkCode: String,
        s2s: S2SConfig,
        bidders: [String: BidderEntry]? = nil,
        amazonAps: GlobalAmazonConfig?,
        useTestAds: Bool = false,
        refresh: RefreshConfig? = nil,
        placements: [PlacementConfig]
    ) {
        self.propertyId = propertyId
        self.appName = appName
        self.gamNetworkCode = gamNetworkCode
        self.s2s = s2s
        self.bidders = bidders
        self.amazonAps = amazonAps
        self.useTestAds = useTestAds
        self.refresh = refresh
        self.placements = placements
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        propertyId = try container.decode(String.self, forKey: .propertyId)
        appName = try container.decode(String.self, forKey: .appName)
        gamNetworkCode = try container.decode(String.self, forKey: .gamNetworkCode)
        s2s = try container.decode(S2SConfig.self, forKey: .s2s)
        bidders = try container.decodeIfPresent([String: BidderEntry].self, forKey: .bidders)
        amazonAps = try container.decodeIfPresent(GlobalAmazonConfig.self, forKey: .amazonAps)
        useTestAds = try container.decodeIfPresent(Bool.self, forKey: .useTestAds) ?? false
        refresh = try container.decodeIfPresent(RefreshConfig.self, forKey: .refresh)
        placements = try container.decode([PlacementConfig].self, forKey: .placements)
    }
}

/**
 * S2S (server-to-server) auction configuration
 *
 * Controls the connection to the BigCrunch S2S endpoint for bid requests.
 */
public struct S2SConfig: Codable {
    public let enabled: Bool
    public let serverUrl: String
    public let timeoutMs: Int

    public init(enabled: Bool = true, serverUrl: String, timeoutMs: Int) {
        self.enabled = enabled
        self.serverUrl = serverUrl
        self.timeoutMs = timeoutMs
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        enabled = try container.decodeIfPresent(Bool.self, forKey: .enabled) ?? true
        serverUrl = try container.decode(String.self, forKey: .serverUrl)
        timeoutMs = try container.decode(Int.self, forKey: .timeoutMs)
    }
}

/**
 * Top-level bidder entry with shared params and per-placement params
 *
 * Each bidder has optional shared params (applied to all impressions) and
 * a placements map (placementId → imp-level params for that bidder).
 */
public struct BidderEntry: Codable {
    public let params: [String: AnyCodable]?
    public let placements: [String: [String: AnyCodable]]?

    public init(params: [String: AnyCodable]?, placements: [String: [String: AnyCodable]]?) {
        self.params = params
        self.placements = placements
    }
}

/**
 * Global Amazon APS configuration
 */
public struct GlobalAmazonConfig: Codable {
    public let enabled: Bool
    public let pubId: String
    public let timeout: Int

    public init(enabled: Bool, pubId: String, timeout: Int) {
        self.enabled = enabled
        self.pubId = pubId
        self.timeout = timeout
    }
}
