#!/usr/bin/env python3
"""
Minecraft Username Sniper - Auto-Reclaim (Phase 5 Feature #4)

Monitor previously-owned usernames and automatically snipe if they become available.
Polls namemc.com to check availability, then triggers a snipe when a name drops.

Usage:
    python3 sniper_autoreclaim.py --tokens-file tokens.txt --interval 60
    python3 sniper_autoreclaim.py --accounts-file accounts.txt --webhook-url https://discord.com/api/webhooks/...

Names are tracked in the snipe_history.db (my_names table).
"""

import argparse
import json
import logging
import signal
import sys
import threading
import time
from datetime import datetime, timezone

import httpx

try:
    from sniper_history import get_released_names, register_my_name, get_all_my_names
    from sniper_webhook import send_snipe_alert, send_info_alert
except ImportError:
    pass


# --- Name availability checker ---

def check_namemc_available(username):
    """Check if a username is available via namemc.com API.

    Returns:
        True = available (no profile found), False = taken, None = error
    """
    try:
        # namemc API returns 404 if username doesn't exist (available)
        resp = httpx.get(f"https://namemc.com/api/v2/user/{username}", timeout=10.0)
        if resp.status_code == 404:
            return True  # No profile = available
        elif resp.status_code == 200:
            data = resp.json()
            # If profile exists with a UUID, it's taken
            return data.get("id") is None
        return None
    except Exception:
        return None


def check_minecraft_api_available(username):
    """Check via official Minecraft API.

    Returns:
        True = available (404), False = taken (200 with UUID), None = error
    """
    try:
        resp = httpx.get(
            f"https://api.minecraftservices.com/minecraft/profile/{username}",
            timeout=10.0,
        )
        if resp.status_code == 404:
            return True  # No profile = available
        elif resp.status_code == 200:
            return False  # Has UUID = taken
        return None
    except Exception:
        return None


def check_name_available(username):
    """Check availability using both sources for reliability."""
    namemc = check_namemc_available(username)
    mc_api = check_minecraft_api_available(username)

    if namemc is True and mc_api is True:
        return True
    elif namemc is False or mc_api is False:
        return False
    # If one says available and other is uncertain, trust namemc
    return namemc if namemc is not None else mc_api


# --- Auto-reclaim monitor ---

class AutoReclaimMonitor:
    """Monitor released names and auto-snip when they become available."""

    def __init__(self, tokens, interval=60, webhook_url=None, logger=None):
        """
        Args:
            tokens: List of bearer tokens to use for sniping
            interval: Check interval in seconds
            webhook_url: Discord webhook URL for alerts
            logger: Log function
        """
        self.tokens = tokens
        self.interval = interval
        self.webhook_url = webhook_url
        self.log = logger or print
        self.stop_event = threading.Event()
        self.names_being_sniped = set()  # Prevent double-sniping

    def run(self):
        """Start monitoring loop."""
        self.log(f"[*] Auto-reclaim monitor started (interval: {self.interval}s)")
        self.log(f"[*] Monitoring released names for re-sniping")

        while not self.stop_event.is_set():
            try:
                self._check_all_names()
            except Exception as e:
                self.log(f"[ERROR] Monitor error: {e}")

            # Sleep in small increments to respond to stop quickly
            for _ in range(self.interval * 10):
                if self.stop_event.is_set():
                    break
                time.sleep(0.1)

        self.log("[*] Auto-reclaim monitor stopped")

    def _check_all_names(self):
        """Check all released names for availability."""
        try:
            released = get_released_names()
        except Exception:
            return

        if not released:
            return

        for entry in released:
            name = entry["username"]
            if self.stop_event.is_set():
                break
            if name in self.names_being_sniped:
                continue

            available = check_name_available(name)
            if available is True:
                self.log(f"\n{'='*60}")
                self.log(f"[🔔] NAME AVAILABLE: {name}!")
                self.log(f"[🔔] Previously released: {entry.get('released_date', 'unknown')}")
                self.log(f"{'='*60}")

                # Alert
                if self.webhook_url and send_info_alert:
                    send_info_alert(
                        self.webhook_url,
                        f"🔔 Name Available: {name}",
                        f"A previously-owned name just became available!",
                        fields=[
                            {"name": "Released", "value": entry.get("released_date", "unknown"), "inline": True},
                            {"name": "Action", "value": "Snipe it manually or use headless mode", "inline": True},
                        ],
                    )
                    self.log("[WEBHOOK] Alert sent to Discord")
                elif send_info_alert:
                    # Try without webhook
                    pass

                self.names_being_sniped.add(name)

            elif available is False:
                self.log(f"[*] {name}: still taken")
            else:
                self.log(f"[*] {name}: check inconclusive")

    def stop(self):
        self.stop_event.set()


# --- CLI ---

def parse_args():
    parser = argparse.ArgumentParser(
        description="🔄 Minecraft Username Auto-Reclaim Monitor",
        epilog="""
Examples:
  # Monitor with token file
  python3 sniper_autoreclaim.py -t tokens.txt --interval 60

  # With Discord alerts
  python3 sniper_autoreclaim.py -t tokens.txt --webhook-url https://discord.com/api/webhooks/...

  # Manage names
  python3 sniper_autoreclaim.py --add-name Steve
  python3 sniper_autoreclaim.py --release-name Steve
  python3 sniper_autoreclaim.py --list-names
        """,
    )

    parser.add_argument("-t", "--tokens-file", help="File with bearer tokens")
    parser.add_argument("-a", "--accounts-file", help="File with email:password credentials")
    parser.add_argument("--interval", type=int, default=60, help="Check interval in seconds (default: 60)")
    parser.add_argument("--webhook-url", help="Discord webhook URL")

    # Name management
    parser.add_argument("--add-name", help="Register a name as yours")
    parser.add_argument("--release-name", help="Mark a name as released (for monitoring)")
    parser.add_argument("--list-names", action="store_true", help="List all tracked names")

    return parser.parse_args()


def main():
    args = parse_args()

    # Name management commands
    if args.add_name:
        register_my_name(args.add_name)
        print(f"[✓] Registered '{args.add_name}' as your name")
        return

    if args.release_name:
        from sniper_history import release_my_name
        release_my_name(args.release_name)
        print(f"[✓] Marked '{args.release_name}' as released")
        return

    if args.list_names:
        names = get_all_my_names()
        if not names:
            print("No tracked names. Use --add-name to register.")
            return
        print(f"\n{'='*60}")
        print(f"{'Name':<20} {'Status':<12} {'Claimed':<22}")
        print(f"{'='*60}")
        for n in names:
            status = "Active" if n["active"] else "Released"
            print(f"{n['username']:<20} {status:<12} {n.get('claimed_date', '?')[:20]}")
        print(f"{'='*60}\n")
        return

    # Load tokens
    tokens = []
    if args.tokens_file:
        with open(args.tokens_file) as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith("#"):
                    tokens.append(line)

    if not tokens:
        print("[ERROR] No tokens loaded. Use --tokens-file")
        sys.exit(1)

    print(f"[*] Loaded {len(tokens)} tokens")

    # Setup signal handling
    monitor = None

    def signal_handler(sig, frame):
        if monitor:
            monitor.stop()
        sys.exit(0)

    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    # Start monitor
    monitor = AutoReclaimMonitor(
        tokens=tokens,
        interval=args.interval,
        webhook_url=args.webhook_url,
    )
    monitor.run()


if __name__ == "__main__":
    main()
