import CodexBarCore
import Commander
import Foundation

// MARK: - Options & parsing helpers

struct UsageOptions: CommanderParsable {
    private static let sourceHelp: String = {
        #if os(macOS)
        "Data source: auto | web | cli | oauth | api (auto behavior is provider-specific)"
        #else
        "Data source: auto | web | cli | oauth | api (web/auto are macOS only for web-capable providers)"
        #endif
    }()

    @Flag(names: [.short("v"), .long("verbose")], help: "Enable verbose logging")
    var verbose: Bool = false

    @Flag(name: .long("json-output"), help: "Emit machine-readable logs")
    var jsonOutput: Bool = false

    @Option(name: .long("log-level"), help: "Set log level (trace|verbose|debug|info|warning|error|critical)")
    var logLevel: String?

    @Option(
        name: .long("provider"),
        help: ProviderHelp.optionHelp)
    var provider: ProviderSelection?

    @Option(name: .long("account"), help: "Token account label to use (from config.json)")
    var account: String?

    @Option(name: .long("account-index"), help: "Token account index (1-based)")
    var accountIndex: Int?

    @Flag(name: .long("all-accounts"), help: "Fetch all token accounts, or all visible Codex accounts")
    var allAccounts: Bool = false

    @Option(name: .long("format"), help: "Output format: text | json")
    var format: OutputFormat?

    @Flag(name: .long("json"), help: "")
    var jsonShortcut: Bool = false

    @Flag(name: .long("json-only"), help: "Emit JSON only (suppress non-JSON output)")
    var jsonOnly: Bool = false

    @Flag(name: .long("no-credits"), help: "Skip Codex credits line")
    var noCredits: Bool = false

    @Flag(name: .long("no-color"), help: "Disable ANSI colors in text output")
    var noColor: Bool = false

    @Flag(name: .long("pretty"), help: "Pretty-print JSON output")
    var pretty: Bool = false

    @Flag(name: .long("status"), help: "Fetch and include provider status")
    var status: Bool = false

    @Flag(name: .long("web"), help: "Alias for --source web")
    var web: Bool = false

    @Option(name: .long("source"), help: Self.sourceHelp)
    var source: String?

    @Flag(
        name: .long("app-auto-verifier"),
        help: "Exercise the app's Claude Auto route (verification only; requires --provider claude --source auto)")
    var appAutoVerifier: Bool = false

    @Option(name: .long("web-timeout"), help: "Web fetch timeout (seconds; source=auto or web)")
    var webTimeout: Double?

    @Flag(name: .long("web-debug-dump-html"), help: "Dump HTML snapshots to /tmp when Codex dashboard data is missing")
    var webDebugDumpHtml: Bool = false

    @Flag(name: .long("antigravity-plan-debug"), help: "Emit Antigravity planInfo fields (debug)")
    var antigravityPlanDebug: Bool = false

    @Flag(name: .long("augment-debug"), help: "Emit Augment API responses (debug)")
    var augmentDebug: Bool = false
}

struct GuardOptions: CommanderParsable {
    @Flag(names: [.short("v"), .long("verbose")], help: "Enable verbose logging")
    var verbose: Bool = false

    @Flag(name: .long("json-output"), help: "Emit machine-readable logs")
    var jsonOutput: Bool = false

    @Option(name: .long("log-level"), help: "Set log level (trace|verbose|debug|info|warning|error|critical)")
    var logLevel: String?

    @Option(name: .long("provider"), help: ProviderHelp.optionHelp)
    var provider: ProviderSelection?

    @Option(name: .long("min-remaining"), help: "Minimum remaining quota required, as a percent (default 10)")
    var minRemaining: Double?

    @Option(name: .long("window"), help: "Window to check: session (primary) | weekly (secondary)")
    var window: String?

    @Option(
        name: .long("timeout"),
        help: "Overall fetch timeout in seconds, 0...86400 (default 60; 0 disables)")
    var timeout: Double?

    @Flag(name: .long("json"), help: "Emit machine-readable decision JSON")
    var json: Bool = false

    @Flag(name: .long("pretty"), help: "Pretty-print decision JSON")
    var pretty: Bool = false

    @Flag(name: .long("fail-open"), help: "Exit 0 instead of 69 when quota is unavailable")
    var failOpen: Bool = false
}

enum ProviderSelection: ExpressibleFromArgument {
    case single(UsageProvider)
    case both
    case all
    case custom([UsageProvider])

    init?(argument: String) {
        let normalized = argument.lowercased()
        switch normalized {
        case "both":
            self = .both
        case "all":
            self = .all
        default:
            if let provider = ProviderDescriptorRegistry.cliNameMap[normalized] {
                self = .single(provider)
            } else {
                return nil
            }
        }
    }

    init(provider: UsageProvider) {
        self = .single(provider)
    }

    var asList: [UsageProvider] {
        switch self {
        case let .single(provider):
            return [provider]
        case .both:
            let primary = ProviderDescriptorRegistry.all.filter(\ .metadata.isPrimaryProvider)
            if !primary.isEmpty {
                return primary.map(\ .id)
            }
            return ProviderDescriptorRegistry.all.prefix(2).map(\ .id)
        case .all:
            return ProviderDescriptorRegistry.all.map(\ .id)
        case let .custom(providers):
            return providers
        }
    }
}

enum OutputFormat: String, ExpressibleFromArgument {
    case text
    case json

    init?(argument: String) {
        switch argument.lowercased() {
        case "text": self = .text
        case "json": self = .json
        default: return nil
        }
    }
}

enum ProviderHelp {
    static var list: String {
        let names = ProviderDescriptorRegistry.all.map(\ .cli.name)
        return (names + ["both", "all"]).joined(separator: "|")
    }

    static var optionHelp: String {
        "Provider to query: \(self.list)"
    }
}
