import Foundation

public struct MiniMaxUsageSnapshot: Sendable {
    public let planName: String?
    public let availablePrompts: Int?
    public let currentPrompts: Int?
    public let remainingPrompts: Int?
    public let windowMinutes: Int?
    public let usedPercent: Double?
    public let resetsAt: Date?
    public let updatedAt: Date
    public let services: [MiniMaxServiceUsage]?
    public let billingSummary: MiniMaxBillingSummary?
    public let pointsBalance: Double?
    public let subscriptionExpiresAt: Date?
    public let subscriptionRenewsAt: Date?

    public var primaryService: MiniMaxServiceUsage? {
        self.orderedQuotaServices.first
    }

    public var secondaryService: MiniMaxServiceUsage? {
        let services = self.orderedQuotaServices
        guard services.count >= 2 else { return nil }
        return services[1]
    }

    public var tertiaryService: MiniMaxServiceUsage? {
        let services = self.orderedQuotaServices
        guard services.count >= 3 else { return nil }
        return services[2]
    }

    public var orderedQuotaServices: [MiniMaxServiceUsage] {
        guard let services, !services.isEmpty else { return [] }
        return services.enumerated().sorted { lhs, rhs in
            let lhsRank = self.quotaServiceRank(lhs.element, originalIndex: lhs.offset)
            let rhsRank = self.quotaServiceRank(rhs.element, originalIndex: rhs.offset)
            if lhsRank.primary != rhsRank.primary {
                return lhsRank.primary < rhsRank.primary
            }
            if lhsRank.window != rhsRank.window {
                return lhsRank.window < rhsRank.window
            }
            return lhsRank.originalIndex < rhsRank.originalIndex
        }.map(\.element)
    }

    private func quotaServiceRank(
        _ service: MiniMaxServiceUsage,
        originalIndex: Int) -> (primary: Int, window: Int, originalIndex: Int)
    {
        (
            primary: service.isPrimaryTextQuotaLane ? 0 : 1,
            window: self.quotaWindowRank(service),
            originalIndex: originalIndex)
    }

    private func quotaWindowRank(_ service: MiniMaxServiceUsage) -> Int {
        let window = service.windowType.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
        if window == "weekly" {
            return 1
        }
        return 0
    }

    public init(
        planName: String?,
        availablePrompts: Int?,
        currentPrompts: Int?,
        remainingPrompts: Int?,
        windowMinutes: Int?,
        usedPercent: Double?,
        resetsAt: Date?,
        updatedAt: Date,
        services: [MiniMaxServiceUsage]? = nil,
        billingSummary: MiniMaxBillingSummary? = nil,
        pointsBalance: Double? = nil,
        subscriptionExpiresAt: Date? = nil,
        subscriptionRenewsAt: Date? = nil)
    {
        self.planName = planName
        self.availablePrompts = availablePrompts
        self.currentPrompts = currentPrompts
        self.remainingPrompts = remainingPrompts
        self.windowMinutes = windowMinutes
        self.usedPercent = usedPercent
        self.resetsAt = resetsAt
        self.updatedAt = updatedAt
        self.services = services
        self.billingSummary = billingSummary
        self.pointsBalance = pointsBalance
        self.subscriptionExpiresAt = subscriptionExpiresAt
        self.subscriptionRenewsAt = subscriptionRenewsAt
    }

    public func withBillingSummary(_ billingSummary: MiniMaxBillingSummary?) -> MiniMaxUsageSnapshot {
        MiniMaxUsageSnapshot(
            planName: self.planName,
            availablePrompts: self.availablePrompts,
            currentPrompts: self.currentPrompts,
            remainingPrompts: self.remainingPrompts,
            windowMinutes: self.windowMinutes,
            usedPercent: self.usedPercent,
            resetsAt: self.resetsAt,
            updatedAt: self.updatedAt,
            services: self.services,
            billingSummary: billingSummary,
            pointsBalance: self.pointsBalance,
            subscriptionExpiresAt: self.subscriptionExpiresAt,
            subscriptionRenewsAt: self.subscriptionRenewsAt)
    }
}

extension MiniMaxUsageSnapshot {
    public func toUsageSnapshot() -> UsageSnapshot {
        // If we have services array, use that for multi-service support
        if let services = self.services, !services.isEmpty {
            let primaryWindow = self.rateWindow(for: self.primaryService)
            let secondaryWindow = self.rateWindow(for: self.secondaryService)
            let tertiaryWindow = self.rateWindow(for: self.tertiaryService)

            let planName = self.planName?.trimmingCharacters(in: .whitespacesAndNewlines)
            let loginMethod = (planName?.isEmpty ?? true) ? nil : planName
            let identity = ProviderIdentitySnapshot(
                providerID: .minimax,
                accountEmail: nil,
                accountOrganization: nil,
                loginMethod: loginMethod)

            return UsageSnapshot(
                primary: primaryWindow,
                secondary: secondaryWindow,
                tertiary: tertiaryWindow,
                providerCost: self.pointsBalanceSnapshot(),
                details: self.detailSections(),
                subscriptionExpiresAt: self.subscriptionExpiresAt,
                subscriptionRenewsAt: self.subscriptionRenewsAt,
                updatedAt: self.updatedAt,
                identity: identity)
        }

        // Fallback to single-service mode for backward compatibility
        let used = max(0, min(100, self.usedPercent ?? 0))
        let resetDescription = self.limitDescription()
        let primary = RateWindow(
            usedPercent: used,
            windowMinutes: self.windowMinutes,
            resetsAt: self.resetsAt,
            resetDescription: resetDescription)

        let planName = self.planName?.trimmingCharacters(in: .whitespacesAndNewlines)
        let loginMethod = (planName?.isEmpty ?? true) ? nil : planName
        let identity = ProviderIdentitySnapshot(
            providerID: .minimax,
            accountEmail: nil,
            accountOrganization: nil,
            loginMethod: loginMethod)

        return UsageSnapshot(
            primary: primary,
            secondary: nil,
            tertiary: nil,
            providerCost: self.pointsBalanceSnapshot(),
            details: self.detailSections(),
            subscriptionExpiresAt: self.subscriptionExpiresAt,
            subscriptionRenewsAt: self.subscriptionRenewsAt,
            updatedAt: self.updatedAt,
            identity: identity)
    }

    private func detailSections() -> [ProviderDetailSection] {
        var sections: [ProviderDetailSection] = []
        let services = self.orderedQuotaServices
        if !services.isEmpty {
            let nameCounts = Dictionary(grouping: services.map(\.displayName), by: { $0 }).mapValues(\.count)
            sections.append(.makeSection(title: "Quota services", rows: services.map { service in
                let label = (nameCounts[service.displayName] ?? 0) > 1
                    ? "\(service.displayName) · \(service.windowType)"
                    : service.displayName
                let value = service.isUnlimited
                    ? "Unlimited"
                    : "\(service.usage.formatted()) / \(service.limit.formatted())"
                return .makeRow(
                    label: label,
                    value: value,
                    secondaryValue: "\(Self.percentString(service.percent)) · \(service.resetDescription)")
            }))
        }
        if let billing = self.billingSummary {
            var rows: [ProviderDetailSection.Row] = [
                .makeRow(label: "Today tokens", value: billing.todayTokens.formatted()),
                .makeRow(label: "30d tokens", value: billing.last30DaysTokens.formatted()),
                .makeRow(label: "Today cash", value: billing.todayCash.map(Self.cashString) ?? "—"),
                .makeRow(label: "Models", value: "\(billing.topModels.count)"),
            ]
            if let topModel = billing.topModels.first {
                rows.append(.makeRow(label: "Top model", value: topModel.name))
            }
            if let topMethod = billing.topMethods.first {
                rows.append(.makeRow(label: "Top method", value: topMethod.name))
            }
            if let cash = billing.last30DaysCash {
                rows.append(.makeRow(label: "30d cash", value: Self.cashString(cash)))
            }
            sections.append(.makeSection(
                title: "Billing history",
                rows: rows,
                chart: billing.daily.isEmpty ? nil : .makeChart(
                    title: "Daily tokens",
                    unit: "tokens",
                    points: billing.daily.map { ($0.day, Double($0.tokens)) })))
        }
        return sections
    }

    private static func percentString(_ value: Double) -> String {
        value == value.rounded() ? String(format: "%.0f%% used", value) : String(format: "%.1f%% used", value)
    }

    private static func cashString(_ value: Double) -> String {
        String(format: "%.2f", max(0, value))
    }

    private func rateWindow(for service: MiniMaxServiceUsage?) -> RateWindow? {
        guard let service else { return nil }
        let windowMinutes = self.windowMinutes(for: service)
        return RateWindow(
            usedPercent: max(0, min(100, service.percent)),
            windowMinutes: windowMinutes,
            resetsAt: service.resetsAt,
            resetDescription: service.resetDescription)
    }

    private func limitDescription() -> String? {
        guard let availablePrompts, availablePrompts > 0 else {
            return self.windowDescription()
        }

        if let windowDescription = self.windowDescription() {
            return "\(availablePrompts) prompts / \(windowDescription)"
        }
        return "\(availablePrompts) prompts"
    }

    private func windowDescription() -> String? {
        guard let windowMinutes, windowMinutes > 0 else { return nil }
        if windowMinutes % (24 * 60) == 0 {
            let days = windowMinutes / (24 * 60)
            return "\(days) \(days == 1 ? "day" : "days")"
        }
        if windowMinutes % 60 == 0 {
            let hours = windowMinutes / 60
            return "\(hours) \(hours == 1 ? "hour" : "hours")"
        }
        return "\(windowMinutes) \(windowMinutes == 1 ? "minute" : "minutes")"
    }

    private func windowMinutes(for service: MiniMaxServiceUsage) -> Int? {
        let windowType = service.windowType.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)

        // Handle "Today" case - 24 hours = 1440 minutes
        if windowType == "today" {
            return 24 * 60
        }
        if windowType == "weekly" {
            return 7 * 24 * 60
        }

        // Handle time duration formats like "5 hours", "30 minutes", etc.
        let components = windowType.split(separator: " ")
        guard components.count >= 2 else { return nil }

        guard let value = Int(components[0]) else { return nil }
        let unit = components[1].lowercased()

        switch unit {
        case "hour", "hours", "h", "hr", "hrs":
            return value * 60
        case "minute", "minutes", "min", "mins", "m":
            return value
        case "day", "days", "d":
            return value * 24 * 60
        default:
            return nil
        }
    }

    private func pointsBalanceSnapshot() -> ProviderCostSnapshot? {
        guard let pointsBalance, pointsBalance >= 0 else { return nil }
        return ProviderCostSnapshot(
            used: pointsBalance,
            limit: 0,
            currencyCode: "Points",
            period: "MiniMax points balance",
            updatedAt: self.updatedAt)
    }
}
