import CodexBarCore
import Foundation
import Testing

struct GeminiStatusProbeTests {
    /// Sample /stats output from Gemini CLI (actual format with box-drawing chars)
    static let sampleStatsOutput = """
    │  Model Usage                            Reqs                  Usage left  │
    │  ───────────────────────────────────────────────────────────────────────  │
    │  gemini-2.5-flash                          -   99.8% (Resets in 20h 37m)  │
    │  gemini-2.5-flash-lite                     -   99.8% (Resets in 20h 37m)  │
    │  gemini-2.5-pro                            -      100.0% (Resets in 24h)  │
    │  gemini-3-pro-preview                      -      100.0% (Resets in 24h)  │
    """

    // MARK: - Legacy CLI parsing tests (kept for fallback support)

    @Test
    func `parses minimum percent from multiple models`() throws {
        let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
        #expect(snap.dailyPercentLeft == 99.8)
        #expect(snap.resetDescription == "Resets in 20h 37m")
    }

    @Test
    func `parses lower percent correctly`() throws {
        let output = """
        │  Model Usage                                                  Reqs                  Usage left  │
        │  gemini-2.5-flash                                               10       85.5% (Resets in 12h)  │
        │  gemini-2.5-pro                                                  5       92.0% (Resets in 12h)  │
        """
        let snap = try GeminiStatusProbe.parse(text: output)
        #expect(snap.dailyPercentLeft == 85.5)
        #expect(snap.resetDescription == "Resets in 12h")
    }

    @Test
    func `handles zero percent usage`() throws {
        let output = """
        │  gemini-2.5-flash                                               50        0.0% (Resets in 6h)  │
        │  gemini-2.5-pro                                                 20       15.0% (Resets in 6h)  │
        """
        let snap = try GeminiStatusProbe.parse(text: output)
        #expect(snap.dailyPercentLeft == 0.0)
        #expect(snap.resetDescription == "Resets in 6h")
    }

    @Test
    func `handles100 percent remaining`() throws {
        let output = """
        │  gemini-2.5-flash                                                -      100.0% (Resets in 24h)  │
        """
        let snap = try GeminiStatusProbe.parse(text: output)
        #expect(snap.dailyPercentLeft == 100.0)
    }

    @Test
    func `throws on empty output`() {
        #expect(throws: GeminiStatusProbeError.self) {
            try GeminiStatusProbe.parse(text: "")
        }
    }

    @Test
    func `throws on no usage data`() {
        let output = """
        Welcome to Gemini CLI!
        Type /help for available commands.
        """
        #expect(throws: GeminiStatusProbeError.self) {
            try GeminiStatusProbe.parse(text: output)
        }
    }

    @Test
    func `strips ANSI codes before parsing`() throws {
        let output =
            "\u{1B}[32m│\u{1B}[0m  gemini-2.5-flash                                                -       75.5% " +
            "(Resets in 18h)  │"
        let snap = try GeminiStatusProbe.parse(text: output)
        #expect(snap.dailyPercentLeft == 75.5)
    }

    @Test
    func `preserves raw text`() throws {
        let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
        #expect(snap.rawText == Self.sampleStatsOutput)
        #expect(snap.accountEmail == nil) // Legacy parse doesn't extract email
        #expect(snap.accountPlan == nil)
    }

    @Test
    func `parses various reset descriptions`() throws {
        let cases: [(String, String)] = [
            ("Resets in 24h", "Resets in 24h"),
            ("Resets in 1h 30m", "Resets in 1h 30m"),
            ("Resets tomorrow", "Resets tomorrow"),
        ]
        for (resetStr, expected) in cases {
            let output = """
            │  gemini-2.5-flash                                                -       50.0% (\(resetStr))  │
            """
            let snap = try GeminiStatusProbe.parse(text: output)
            #expect(snap.resetDescription == expected)
        }
    }

    @Test
    func `throws not logged in on auth prompt`() {
        let authOutputs = [
            "Waiting for auth... (Press ESC or CTRL+C to cancel)",
            "Login with Google\nUse Gemini API key",
            "Some preamble\nWaiting for auth\nMore text",
        ]
        for output in authOutputs {
            #expect(throws: GeminiStatusProbeError.notLoggedIn) {
                try GeminiStatusProbe.parse(text: output)
            }
        }
    }

    // MARK: - Model quota grouping tests

    @Test
    func `parses models into quota array`() throws {
        let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
        // Should parse multiple models (exact count may change as Google adds/removes models)
        #expect(snap.modelQuotas.count >= 2)

        // All model IDs should start with "gemini"
        for quota in snap.modelQuotas {
            #expect(quota.modelId.hasPrefix("gemini"))
        }
    }

    @Test
    func `lowest percent left returns minimum`() throws {
        let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
        // Flash models are 99.8%, Pro models are 100%, so min should be 99.8
        #expect(snap.lowestPercentLeft == 99.8)
    }

    @Test
    func `tier grouping by keyword`() throws {
        // Test that flash/pro keyword filtering works (model names may change)
        let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)

        let flashQuotas = snap.modelQuotas.filter { $0.modelId.contains("flash") && !$0.modelId.contains("flash-lite") }
        let flashLiteQuotas = snap.modelQuotas.filter { $0.modelId.contains("flash-lite") }
        let proQuotas = snap.modelQuotas.filter { $0.modelId.contains("pro") }

        // Should have at least one of each tier in sample
        #expect(!flashQuotas.isEmpty)
        #expect(!flashLiteQuotas.isEmpty)
        #expect(!proQuotas.isEmpty)
    }

    @Test
    func `tier minimum calculation`() throws {
        // Use controlled test data to verify min-per-tier logic
        // Model names must start with "gemini-" to match the parser regex
        let output = """
        │  gemini-a-flash                            10       85.0% (Resets in 24h)  │
        │  gemini-b-flash                             5       92.0% (Resets in 24h)  │
        │  gemini-c-pro                               2       95.0% (Resets in 24h)  │
        │  gemini-d-pro                               1       99.0% (Resets in 24h)  │
        """
        let snap = try GeminiStatusProbe.parse(text: output)

        let flashQuotas = snap.modelQuotas.filter { $0.modelId.contains("flash") }
        let proQuotas = snap.modelQuotas.filter { $0.modelId.contains("pro") }

        let flashMin = flashQuotas.min(by: { $0.percentLeft < $1.percentLeft })
        let proMin = proQuotas.min(by: { $0.percentLeft < $1.percentLeft })

        #expect(flashMin?.percentLeft == 85.0)
        #expect(proMin?.percentLeft == 95.0)
    }

    @Test
    func `quotas have reset descriptions`() throws {
        let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)

        // At least some quotas should have reset descriptions
        let hasResets = snap.modelQuotas.contains { $0.resetDescription != nil }
        #expect(hasResets)
    }

    @Test
    func `to usage snapshot creates separate flash and flash lite meters`() throws {
        let snap = try GeminiStatusProbe.parse(text: Self.sampleStatsOutput)
        let usage = snap.toUsageSnapshot()

        #expect(usage.primary?.remainingPercent == 100.0)
        #expect(usage.secondary?.remainingPercent == 99.8)
        #expect(usage.tertiary?.remainingPercent == 99.8)
        #expect(usage.primary?.windowMinutes == 1440)
        #expect(usage.secondary?.windowMinutes == 1440)
        #expect(usage.tertiary?.windowMinutes == 1440)
        #expect(usage.secondary?.resetDescription == "Resets in 20h 37m")
        #expect(usage.tertiary?.resetDescription == "Resets in 20h 37m")
    }

    @Test
    func `to usage snapshot does not let flash lite contaminate flash bucket`() throws {
        let output = """
        │  gemini-2.5-flash                           10       91.0% (Resets in 12h)  │
        │  gemini-2.5-flash-lite                       5       33.0% (Resets in 6h)   │
        │  gemini-2.5-pro                              2       80.0% (Resets in 24h)  │
        """
        let snap = try GeminiStatusProbe.parse(text: output)
        let usage = snap.toUsageSnapshot()

        #expect(usage.secondary?.remainingPercent == 91.0)
        #expect(usage.tertiary?.remainingPercent == 33.0)
        #expect(usage.secondary?.resetDescription == "Resets in 12h")
        #expect(usage.tertiary?.resetDescription == "Resets in 6h")
    }

    @Test
    func `to usage snapshot omits tertiary when no flash lite exists`() throws {
        let output = """
        │  gemini-2.5-flash                           10       85.0% (Resets in 12h)  │
        │  gemini-2.5-pro                              2       95.0% (Resets in 24h)  │
        """
        let snap = try GeminiStatusProbe.parse(text: output)
        let usage = snap.toUsageSnapshot()

        #expect(usage.secondary?.remainingPercent == 85.0)
        #expect(usage.tertiary == nil)
    }

    @Test
    func `to usage snapshot uses lowest remaining quota per tier`() throws {
        let output = """
        │  gemini-a-flash                            10       91.0% (Resets in 12h)  │
        │  gemini-b-flash                             5       74.0% (Resets in 10h)  │
        │  gemini-c-flash-lite                        8       66.0% (Resets in 9h)   │
        │  gemini-d-flash-lite                        1       42.0% (Resets in 7h)   │
        │  gemini-e-pro                               2       88.0% (Resets in 24h)  │
        │  gemini-f-pro                               1       97.0% (Resets in 25h)  │
        """
        let snap = try GeminiStatusProbe.parse(text: output)
        let usage = snap.toUsageSnapshot()

        #expect(usage.primary?.remainingPercent == 88.0)
        #expect(usage.secondary?.remainingPercent == 74.0)
        #expect(usage.tertiary?.remainingPercent == 42.0)
        #expect(usage.secondary?.resetDescription == "Resets in 10h")
        #expect(usage.tertiary?.resetDescription == "Resets in 7h")
    }

    // MARK: - Live API test

    @Test
    func `live gemini fetch`() async throws {
        guard ProcessInfo.processInfo.environment["LIVE_GEMINI_FETCH"] == "1" else {
            return
        }
        let probe = GeminiStatusProbe()
        let snap = try await probe.fetch()
        print(
            """
            Live Gemini usage (via API):
            models: \(snap.modelQuotas.map { "\($0.modelId): \($0.percentLeft)%" }.joined(separator: ", "))
            lowest: \(snap.lowestPercentLeft ?? -1)% left
            """)
        #expect(!snap.modelQuotas.isEmpty)
    }
}
