import Foundation

// MARK: - Configuration

/// Configuration for the Synor Economics client.
public struct EconomicsConfig {
    public let apiKey: String
    public let endpoint: String
    public let timeout: TimeInterval
    public let retries: Int
    public let debug: Bool

    public init(
        apiKey: String,
        endpoint: String = "https://economics.synor.io/v1",
        timeout: TimeInterval = 60,
        retries: Int = 3,
        debug: Bool = false
    ) {
        self.apiKey = apiKey
        self.endpoint = endpoint
        self.timeout = timeout
        self.retries = retries
        self.debug = debug
    }
}

// MARK: - Enums

/// Service types for pricing.
public enum ServiceType: String, Codable, Sendable {
    case compute
    case storage
    case database
    case hosting
    case bridge
    case mining
    case rpc
}

/// Billing period.
public enum BillingPeriod: String, Codable, Sendable {
    case daily
    case weekly
    case monthly
    case yearly
}

/// Stake status.
public enum StakeStatus: String, Codable, Sendable {
    case active
    case unstaking
    case unlocked
    case slashed
}

/// Discount type.
public enum DiscountType: String, Codable, Sendable {
    case percentage
    case fixed
    case volume
}

// MARK: - Pricing Types

/// Usage metrics for pricing calculation.
public struct UsageMetrics: Codable, Sendable {
    public var computeHours: Double
    public var storageGb: Double
    public var requests: Int64
    public var bandwidthGb: Double
    public var gpuHours: Double

    public init(
        computeHours: Double = 0,
        storageGb: Double = 0,
        requests: Int64 = 0,
        bandwidthGb: Double = 0,
        gpuHours: Double = 0
    ) {
        self.computeHours = computeHours
        self.storageGb = storageGb
        self.requests = requests
        self.bandwidthGb = bandwidthGb
        self.gpuHours = gpuHours
    }
}

/// Pricing tier.
public struct PricingTier: Codable, Sendable {
    public let name: String
    public let upTo: Double
    public let pricePerUnit: Double
    public let unit: String
}

/// Price result.
public struct Price: Codable, Sendable {
    public let service: ServiceType
    public let amount: String
    public let currency: String
    public let amountUsd: String
    public let breakdown: [PricingTier]?
    public let discount: String?
    public let finalAmount: String
}

/// Usage plan for cost estimation.
public struct UsagePlan: Codable, Sendable {
    public let service: ServiceType
    public let estimatedUsage: UsageMetrics
    public let period: BillingPeriod

    public init(service: ServiceType, estimatedUsage: UsageMetrics, period: BillingPeriod) {
        self.service = service
        self.estimatedUsage = estimatedUsage
        self.period = period
    }
}

/// Cost breakdown item.
public struct CostBreakdown: Codable, Sendable {
    public let category: String
    public let amount: String
    public let percentage: Double
}

/// Cost estimate result.
public struct CostEstimate: Codable, Sendable {
    public let service: ServiceType
    public let period: BillingPeriod
    public let estimatedCost: String
    public let estimatedCostUsd: String
    public let currency: String
    public let confidenceLevel: String
    public let breakdown: [CostBreakdown]?
    public let savingsFromStaking: String?
}

// MARK: - Billing Types

/// Usage record.
public struct UsageRecord: Codable, Sendable {
    public let service: ServiceType
    public let resource: String
    public let quantity: Double
    public let unit: String
    public let timestamp: Int64
    public let cost: String
}

/// Usage summary.
public struct Usage: Codable, Sendable {
    public let period: BillingPeriod
    public let startDate: Int64
    public let endDate: Int64
    public let records: [UsageRecord]
    public let totalCost: String
    public let currency: String
}

/// Invoice line item.
public struct InvoiceLineItem: Codable, Sendable {
    public let description: String
    public let quantity: Double
    public let unit: String
    public let unitPrice: String
    public let amount: String
}

/// Invoice.
public struct Invoice: Codable, Sendable {
    public let id: String
    public let number: String
    public let periodStart: Int64
    public let periodEnd: Int64
    public let subtotal: String
    public let tax: String
    public let discount: String
    public let total: String
    public let currency: String
    public let status: String
    public let dueDate: Int64?
    public let paidAt: Int64?
    public let lineItems: [InvoiceLineItem]
    public let pdfUrl: String?
}

/// Account balance.
public struct AccountBalance: Codable, Sendable {
    public let available: String
    public let pending: String
    public let reserved: String
    public let staked: String
    public let currency: String
    public let lastUpdated: Int64
}

// MARK: - Staking Types

/// Stake receipt.
public struct StakeReceipt: Codable, Sendable {
    public let id: String
    public let txHash: String
    public let amount: String
    public let startDate: Int64
    public let endDate: Int64
    public let apy: Double
    public let status: StakeStatus
}

/// Stake info.
public struct StakeInfo: Codable, Sendable {
    public let id: String
    public let amount: String
    public let status: StakeStatus
    public let startDate: Int64
    public let endDate: Int64
    public let apy: Double
    public let earnedRewards: String
    public let pendingRewards: String
    public let unlockDate: Int64?
    public let discountPercent: Int
}

/// Unstake receipt.
public struct UnstakeReceipt: Codable, Sendable {
    public let id: String
    public let txHash: String
    public let amount: String
    public let initiatedAt: Int64
    public let availableAt: Int64
    public let penalty: String?
}

/// Reward record.
public struct RewardRecord: Codable, Sendable {
    public let date: Int64
    public let amount: String
    public let type: String
    public let txHash: String?
}

/// Staking rewards.
public struct Rewards: Codable, Sendable {
    public let totalEarned: String
    public let pendingClaim: String
    public let lastClaimDate: String?
    public let currentApy: Double
    public let history: [RewardRecord]
}

// MARK: - Discount Types

/// Discount.
public struct Discount: Codable, Sendable {
    public let id: String
    public let code: String
    public let type: DiscountType
    public let value: String
    public let validFrom: Int64?
    public let validUntil: Int64?
    public let usageLimit: Int?
    public let usageCount: Int
    public let applicableServices: [ServiceType]?
    public let minimumSpend: String?
    public let active: Bool
}

/// Applied discount.
public struct AppliedDiscount: Codable, Sendable {
    public let discount: Discount
    public let savedAmount: String
    public let appliedAt: Int64
}

// MARK: - Response Types

struct TiersResponse: Codable {
    let tiers: [PricingTier]?
}

struct InvoicesResponse: Codable {
    let invoices: [Invoice]?
}

struct StakesResponse: Codable {
    let stakes: [StakeInfo]?
}

struct DiscountsResponse: Codable {
    let discounts: [Discount]?
}

struct AppliedDiscountsResponse: Codable {
    let discounts: [AppliedDiscount]?
}

// MARK: - Error

/// Error thrown by Economics operations.
public struct EconomicsError: Error, LocalizedError {
    public let message: String
    public let code: String?
    public let statusCode: Int

    public init(message: String, code: String? = nil, statusCode: Int = 0) {
        self.message = message
        self.code = code
        self.statusCode = statusCode
    }

    public var errorDescription: String? { message }
}
