"""wzrd CLI — earn rewards by reporting AI model velocity signals.

Usage:
    wzrd earn                             # start earning (default keypair)
    wzrd earn --tasks=code,reasoning      # specific tasks
    wzrd earn --keypair=./agent.json      # explicit keypair
    wzrd pick code                        # best model for task (no auth)
    wzrd shortlist reasoning --limit=5    # ranked models (no auth)
    wzrd earned                           # check earnings (auth required)
    wzrd status                           # full agent status (auth required)
"""

from __future__ import annotations

import argparse
import json
import sys

MIN_CYCLE_SECONDS = 30


def cmd_run(args: argparse.Namespace) -> None:
    from .loop import run_loop

    tasks = [t.strip() for t in args.tasks.split(",") if t.strip()]
    if not tasks:
        sys.exit("error: --tasks cannot be empty")

    cycle = args.cycle
    if cycle < MIN_CYCLE_SECONDS:
        import logging
        logging.basicConfig(level=logging.INFO, format="%(asctime)s [wzrd] %(message)s", datefmt="%H:%M:%S")
        logging.getLogger("wzrd").warning("cycle clamped to %d (minimum)", MIN_CYCLE_SECONDS)

    run_loop(
        keypair=args.keypair,
        tasks=tasks,
        cycle_seconds=cycle,
        max_cycles=args.max_cycles,
        claim=not args.no_claim,
        stake=args.auto_stake,
        quiet=args.quiet,
    )


def cmd_pick(args: argparse.Namespace) -> None:
    from .client import pick
    print(pick(args.task))


def cmd_shortlist(args: argparse.Namespace) -> None:
    from .client import shortlist
    results = shortlist(args.task, limit=args.limit)
    for i, r in enumerate(results):
        print(f"#{i+1}  {r.model:<40s}  score={r.score:.2f}  trend={r.trend}  platform={r.platform}")


def cmd_earned(args: argparse.Namespace) -> None:
    from .agent import WZRDAgent
    from .loop import _fetch_ccm_price, _fmt_ccm

    agent = WZRDAgent()
    agent.authenticate(args.keypair)
    earned = agent.earned()

    ccm_price = _fetch_ccm_price()
    total = earned.get("total_earned_ccm", 0)
    pending = earned.get("pending_ccm", 0)
    rank = earned.get("rank")
    streak = earned.get("contribution_streak_days", 0)
    pipeline = earned.get("pipeline", {})

    print(f"Agent: {earned.get('agent_pubkey', '?')}")
    print(f"Lifetime: {_fmt_ccm(total, ccm_price)}")
    if pending > 0:
        print(f"Pending: {_fmt_ccm(pending, ccm_price)}")
    if rank:
        print(f"Rank: #{rank}", end="")
        if streak > 1:
            print(f" | Streak: {streak} days", end="")
        print()
    state = pipeline.get("state")
    hint = pipeline.get("hint")
    if state:
        relay = "relay ready" if pipeline.get("relay_ready") else "waiting"
        print(f"Pipeline: {state} — {relay}")
    if hint:
        print(f"  {hint}")


def cmd_status(args: argparse.Namespace) -> None:
    from .agent import WZRDAgent

    agent = WZRDAgent()
    agent.authenticate(args.keypair)
    status = agent.status()
    print(json.dumps(status, indent=2, default=str))


def cmd_stake(args: argparse.Namespace) -> None:
    from .agent import WZRDAgent, SLOTS_PER_DAY

    agent = WZRDAgent()
    agent.authenticate(args.keypair)

    lock_slots = int(args.lock * SLOTS_PER_DAY)

    # Handle "all" / "max" as amount
    if isinstance(args.amount, str) and args.amount.lower() in ("all", "max"):
        balance = agent.ccm_balance()
        if balance <= 0:
            sys.exit("error: no CCM balance to stake")
        amount = balance
        human_amount = amount / 1_000_000_000
        print(f"Staking full balance: {human_amount:,.2f} CCM")
    else:
        human_amount = float(args.amount)
        amount = int(human_amount * 1_000_000_000)  # CCM has 9 decimals (Token-2022)

    try:
        result = agent.stake(
            amount=amount,
            lock_duration=lock_slots,
            channel_config=args.channel,
        )
    except Exception as e:
        msg = str(e)
        if "500" in msg or "failed" in msg.lower():
            # Common cause: trying to stake more than balance (transfer fee eats ~0.5%)
            print(f"Stake failed. Try 'wzrd stake all --lock={int(args.lock)}' to stake your full balance.")
            print(f"  (CCM has a 0.5% transfer fee, so staking 1000 CCM requires ~1005 in your wallet)")
            sys.exit(1)
        raise
    tx = result.get("tx_sig", "?")

    # Boost lookup
    boost = "1.0x"
    if args.lock >= 365: boost = "3.0x"
    elif args.lock >= 180: boost = "2.5x"
    elif args.lock >= 90: boost = "2.0x"
    elif args.lock >= 30: boost = "1.5x"
    elif args.lock >= 7: boost = "1.25x"

    print(f"Staked {human_amount:,.0f} CCM (lock: {int(args.lock)}d, boost: {boost}, 7% APR) | tx: {tx}")


def cmd_unstake(args: argparse.Namespace) -> None:
    from .agent import WZRDAgent

    agent = WZRDAgent()
    agent.authenticate(args.keypair)
    result = agent.unstake(channel_config=args.channel)
    tx = result.get("tx_sig", "?")
    print(f"Unstaked | tx: {tx}")


def cmd_rewards(args: argparse.Namespace) -> None:
    from .agent import WZRDAgent, SLOTS_PER_DAY

    agent = WZRDAgent()
    agent.authenticate(args.keypair)

    if args.claim:
        result = agent.claim_staking_rewards(channel_config=args.channel)
        tx = result.get("tx_sig", "?")
        print(f"Claimed staking rewards | tx: {tx}")
        return

    status = agent.staking_status(channel_config=args.channel)
    if not status.get("staked"):
        print("Not staked.")
        return

    amount = status.get("amount", 0)
    lock_end = status.get("lock_end_slot", 0)
    multiplier = status.get("multiplier_bps", 10000)
    pending = status.get("pending_rewards", 0)
    pool = status.get("pool", {})

    boost = f"{multiplier / 10000:.1f}x"
    amount_human = f"{amount / 1_000_000_000:,.3f}".rstrip("0").rstrip(".")
    pending_human = f"{pending / 1_000_000_000:,.3f}".rstrip("0").rstrip(".") if pending else "0"

    print(f"Staked: {amount_human} CCM | Boost: {boost} | Pending rewards: {pending_human} CCM")
    if pool:
        total = pool.get("total_staked", 0)
        stakers = pool.get("staker_count", 0)
        total_human = f"{total / 1_000_000_000:,.3f}".rstrip("0").rstrip(".")
        print(f"Pool: {total_human} CCM total | {stakers} stakers")


def main() -> None:
    parser = argparse.ArgumentParser(
        prog="wzrd",
        description="Earn CCM by reporting AI model velocity signals on Solana.",
    )
    sub = parser.add_subparsers(dest="command")

    # wzrd run
    p_run = sub.add_parser("run", help="Start the earn loop")
    p_run.add_argument("--keypair", default=None, help="Path to Solana keypair (default: auto-detect)")
    p_run.add_argument("--tasks", default="code,chat,reasoning", help="Comma-separated task types")
    p_run.add_argument("--cycle", type=int, default=300, help="Seconds between cycles (min 30)")
    p_run.add_argument("--max-cycles", type=int, default=None, help="Stop after N cycles (default: forever)")
    p_run.add_argument("--no-claim", action="store_true", help="Disable auto-claiming")
    p_run.add_argument("--stake", dest="auto_stake", action="store_true", help="Auto-stake claimed CCM (7-day lock, 1.25x boost, gasless)")
    p_run.add_argument("--quiet", action="store_true", help="Suppress info logging")

    # wzrd pick <task>
    p_pick = sub.add_parser("pick", help="Best model for a task (no auth)")
    p_pick.add_argument("task", default="general", nargs="?", help="Task type (code, chat, reasoning, etc.)")

    # wzrd shortlist <task>
    p_short = sub.add_parser("shortlist", help="Ranked models for a task (no auth)")
    p_short.add_argument("task", default="general", nargs="?", help="Task type")
    p_short.add_argument("--limit", type=int, default=5, help="Number of results")

    # wzrd earned
    p_earned = sub.add_parser("earned", help="Check earned CCM (auth required)")
    p_earned.add_argument("--keypair", default=None, help="Path to Solana keypair")

    # wzrd status
    p_status = sub.add_parser("status", help="Full agent status (auth required)")
    p_status.add_argument("--keypair", default=None, help="Path to Solana keypair")

    # wzrd stake <amount>
    p_stake = sub.add_parser("stake", help="Stake CCM for boost (gasless)")
    p_stake.add_argument("amount", help="CCM amount to stake (or 'all' for full balance)")
    p_stake.add_argument("--lock", type=float, default=0, help="Lock period in days (0=no lock, 30=1.5x, 90=2x, 365=3x)")
    p_stake.add_argument("--channel", default=None, help="Channel config pubkey (default: server default)")
    p_stake.add_argument("--keypair", default=None, help="Path to Solana keypair")

    # wzrd unstake
    p_unstake = sub.add_parser("unstake", help="Unstake CCM (lock must be expired)")
    p_unstake.add_argument("--channel", default=None, help="Channel config pubkey")
    p_unstake.add_argument("--keypair", default=None, help="Path to Solana keypair")

    # wzrd rewards
    p_rewards = sub.add_parser("rewards", help="Check or claim staking rewards")
    p_rewards.add_argument("--claim", action="store_true", help="Claim pending rewards")
    p_rewards.add_argument("--channel", default=None, help="Channel config pubkey")
    p_rewards.add_argument("--keypair", default=None, help="Path to Solana keypair")

    # Aliases: "wzrd earn" = "wzrd run" (with default 3 cycles for a quick demo)
    if len(sys.argv) > 1 and sys.argv[1] in ("earn", "auto", "loop", "start"):
        original = sys.argv[1]
        sys.argv[1] = "run"
        if original == "earn" and "--max-cycles" not in " ".join(sys.argv):
            sys.argv.insert(2, "--max-cycles=3")

    args = parser.parse_args()

    if args.command is None:
        parser.print_help()
        print("\nQuick start:")
        print("  wzrd earn           # start earning (picks models, reports, claims)")
        print("  wzrd pick code      # best model for a task (free, no auth)")
        sys.exit(0)

    try:
        {
            "run": cmd_run,
            "pick": cmd_pick,
            "shortlist": cmd_shortlist,
            "earned": cmd_earned,
            "status": cmd_status,
            "stake": cmd_stake,
            "unstake": cmd_unstake,
            "rewards": cmd_rewards,
        }[args.command](args)
    except KeyboardInterrupt:
        pass
    except Exception as exc:
        # Clean error message, no traceback for known errors
        from .client import WZRDError
        if isinstance(exc, WZRDError):
            sys.exit(f"error: {exc}")
        raise


if __name__ == "__main__":
    main()
