import Foundation

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

public struct MoonshotUsageSnapshot: Sendable {
    public let summary: MoonshotUsageSummary

    public init(summary: MoonshotUsageSummary) {
        self.summary = summary
    }

    public func toUsageSnapshot() -> UsageSnapshot {
        self.summary.toUsageSnapshot()
    }
}

public struct MoonshotUsageSummary: Sendable {
    public let availableBalance: Double
    public let voucherBalance: Double
    public let cashBalance: Double
    public let updatedAt: Date

    public init(
        availableBalance: Double, voucherBalance: Double, cashBalance: Double, updatedAt: Date)
    {
        self.availableBalance = availableBalance
        self.voucherBalance = voucherBalance
        self.cashBalance = cashBalance
        self.updatedAt = updatedAt
    }

    public func toUsageSnapshot() -> UsageSnapshot {
        let balance = UsageFormatter.usdString(self.availableBalance)
        let loginMethod: String
        if self.cashBalance < 0 {
            let deficit = UsageFormatter.usdString(abs(self.cashBalance))
            loginMethod = "Balance: \(balance) · \(deficit) in deficit"
        } else {
            loginMethod = "Balance: \(balance)"
        }
        let identity = ProviderIdentitySnapshot(
            providerID: .moonshot,
            accountEmail: nil,
            accountOrganization: nil,
            loginMethod: loginMethod)
        return UsageSnapshot(
            primary: nil,
            secondary: nil,
            tertiary: nil,
            providerCost: nil,
            updatedAt: self.updatedAt,
            identity: identity)
    }
}

private struct MoonshotBalanceResponse: Decodable {
    let code: Int
    let data: MoonshotBalanceData
    let scode: String
    let status: Bool
}

private struct MoonshotBalanceData: Decodable {
    let availableBalance: Double
    let voucherBalance: Double
    let cashBalance: Double

    private enum CodingKeys: String, CodingKey {
        case availableBalance = "available_balance"
        case voucherBalance = "voucher_balance"
        case cashBalance = "cash_balance"
    }
}

public enum MoonshotUsageError: LocalizedError, Sendable {
    case missingCredentials
    case networkError(String)
    case apiError(String)
    case parseFailed(String)

    public var errorDescription: String? {
        switch self {
        case .missingCredentials:
            "Missing Moonshot API key."
        case let .networkError(message):
            "Moonshot network error: \(message)"
        case let .apiError(message):
            "Moonshot API error: \(message)"
        case let .parseFailed(message):
            "Failed to parse Moonshot response: \(message)"
        }
    }
}

public struct MoonshotUsageFetcher: Sendable {
    private static let log = CodexBarLog.logger(LogCategories.provider(.moonshot, scope: "usage"))
    private static let timeoutSeconds: TimeInterval = 15

    public static func fetchUsage(
        apiKey: String,
        region: MoonshotRegion = .international,
        session transport: any ProviderHTTPTransport = ProviderHTTPClient.shared) async throws -> MoonshotUsageSnapshot
    {
        let cleaned = apiKey.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !cleaned.isEmpty else {
            throw MoonshotUsageError.missingCredentials
        }

        var request = URLRequest(url: self.resolveBalanceURL(region: region))
        request.httpMethod = "GET"
        request.setValue("Bearer \(cleaned)", forHTTPHeaderField: "Authorization")
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        request.timeoutInterval = Self.timeoutSeconds

        let response: ProviderHTTPResponse
        do {
            response = try await transport.response(for: request)
        } catch let error as URLError where error.code == .badServerResponse {
            throw MoonshotUsageError.networkError("Invalid response")
        } catch {
            throw error
        }

        guard response.statusCode == 200 else {
            Self.log.error("Moonshot API returned HTTP \(response.statusCode)")
            throw MoonshotUsageError.apiError("HTTP \(response.statusCode)")
        }

        let summary = try self.parseSummary(data: response.data)
        return MoonshotUsageSnapshot(summary: summary)
    }

    public static func resolveBalanceURL(region: MoonshotRegion) -> URL {
        region.balanceURL
    }

    static func _parseSummaryForTesting(_ data: Data) throws -> MoonshotUsageSummary {
        try self.parseSummary(data: data)
    }

    private static func parseSummary(data: Data) throws -> MoonshotUsageSummary {
        let response: MoonshotBalanceResponse
        do {
            response = try JSONDecoder().decode(MoonshotBalanceResponse.self, from: data)
        } catch {
            throw MoonshotUsageError.parseFailed(error.localizedDescription)
        }

        guard response.code == 0, response.status else {
            throw MoonshotUsageError.apiError("code \(response.code), scode \(response.scode)")
        }

        return MoonshotUsageSummary(
            availableBalance: response.data.availableBalance,
            voucherBalance: response.data.voucherBalance,
            cashBalance: response.data.cashBalance,
            updatedAt: Date())
    }
}
