#!/usr/bin/env python3
"""
SC2 Replay Analyzer CLI

Analyze your StarCraft II replays with filtering, stats, and beautiful terminal output.

Usage:
    sc2                    # Auto-scan for new replays, then interactive mode
    sc2 show               # One-time query of recent games
    sc2 stats              # Show aggregate statistics
    sc2 scan               # Full scan with progress display
    sc2 config             # Re-run setup / change settings
    sc2 export             # Export to CSV
"""
import argparse
import csv
import sys
from pathlib import Path

from . import db
from .config import (
    config_exists,
    load_config,
    save_config,
    find_replay_folders,
    find_matching_players,
    validate_player_name,
    get_player_name,
    get_replay_folder,
    get_display_columns,
    set_display_columns,
    add_display_columns,
    remove_display_columns,
    reset_display_columns,
    is_server_enabled,
    get_server_port,
    DEFAULT_CONFIG,
    AVAILABLE_COLUMNS,
)
from .parser import parse_replay, sha1
from . import ui


def find_replays(folder: str) -> list:
    """Find all .SC2Replay files in folder."""
    folder_path = Path(folder)
    if not folder_path.exists():
        ui.console.print(f"[red]Replay folder not found:[/red] {folder}")
        sys.exit(1)

    replays = list(folder_path.glob("*.SC2Replay"))
    return sorted(replays, key=lambda p: p.stat().st_mtime, reverse=True)


def auto_scan(silent: bool = False) -> int:
    """Automatically scan for new replays. Returns count of new replays found.

    Args:
        silent: If True, don't print any messages (for background scanning)
    """
    replay_folder = get_replay_folder()
    player_name = get_player_name()

    folder_path = Path(replay_folder)
    if not folder_path.exists():
        return 0

    replays = list(folder_path.glob("*.SC2Replay"))
    new_count = 0

    for replay_path in replays:
        replay_id = sha1(str(replay_path))
        if not db.replay_exists(replay_id):
            try:
                data = parse_replay(str(replay_path), player_name)
                if data:
                    db.insert_replay(data)
                    new_count += 1
            except Exception:
                pass  # Silently skip errors during auto-scan

    if new_count > 0:
        # Detect unranked games after adding new replays
        db.detect_unranked_games()
        if not silent:
            ui.console.print(f"[green]Found {new_count} new replay(s)[/green]")

    return new_count


def run_setup_wizard() -> bool:
    """
    Run the first-time setup wizard.
    Returns True if setup completed successfully.
    """
    ui.console.print()
    ui.console.print("[bold cyan]Welcome to SC2 Replay Analyzer![/bold cyan]")
    ui.console.print()
    ui.console.print("Let's set up your configuration.")
    ui.console.print()

    config = DEFAULT_CONFIG.copy()

    # Step 1: Find replay folder
    ui.console.print("[bold]Searching for SC2 replay folders...[/bold]")
    folders = find_replay_folders()

    if not folders:
        ui.console.print("[yellow]No replay folders found automatically.[/yellow]")
        ui.console.print()
        replay_folder = ui.console.input("Enter the path to your Replays/Multiplayer folder: ").strip()
        if not Path(replay_folder).exists():
            ui.console.print(f"[red]Folder not found:[/red] {replay_folder}")
            return False
    elif len(folders) == 1:
        ui.console.print(f"[green]Found:[/green] {folders[0]}")
        ui.console.print()
        use_it = ui.console.input("Use this folder? [Y/n]: ").strip().lower()
        if use_it in ('n', 'no'):
            replay_folder = ui.console.input("Enter the path to your Replays/Multiplayer folder: ").strip()
        else:
            replay_folder = folders[0]
    else:
        ui.console.print("[green]Found multiple replay folders:[/green]")
        for i, folder in enumerate(folders, 1):
            ui.console.print(f"  {i}. {folder}")
        ui.console.print()
        choice = ui.console.input(f"Choose folder [1-{len(folders)}]: ").strip()
        try:
            idx = int(choice) - 1
            if 0 <= idx < len(folders):
                replay_folder = folders[idx]
            else:
                ui.console.print("[red]Invalid choice.[/red]")
                return False
        except ValueError:
            ui.console.print("[red]Invalid choice.[/red]")
            return False

    config["replay_folder"] = replay_folder
    ui.console.print()

    # Step 2: Get player name
    search_name = ui.console.input("Enter your SC2 player name: ").strip()
    if not search_name:
        ui.console.print("[red]Player name cannot be empty.[/red]")
        return False

    # Step 3: Find matching player names
    ui.console.print()
    ui.console.print("[bold]Searching for player name...[/bold]")
    matches, checked = find_matching_players(search_name, replay_folder)

    if not matches:
        if checked > 0:
            ui.console.print(f"[yellow]Warning: No player matching \"{search_name}\" found in {checked} recent replays.[/yellow]")
            confirm = ui.console.input("Continue anyway? [y/N]: ").strip().lower()
            if confirm not in ('y', 'yes'):
                return False
            player_name = search_name
        else:
            ui.console.print("[yellow]Could not validate player name (no replays found).[/yellow]")
            player_name = search_name
    elif len(matches) == 1:
        # Exactly one match
        player_name = list(matches.keys())[0]
        count = matches[player_name]
        ui.console.print(f"[green]Found \"{player_name}\" in {count}/{checked} recent replays[/green]")
    else:
        # Multiple matches - let user choose
        ui.console.print(f"[green]Found multiple matching players:[/green]")
        sorted_matches = sorted(matches.items(), key=lambda x: -x[1])  # Sort by count desc
        for i, (name, count) in enumerate(sorted_matches, 1):
            ui.console.print(f"  {i}. {name} ({count} games)")
        ui.console.print()
        choice = ui.console.input(f"Choose player [1-{len(sorted_matches)}]: ").strip()
        try:
            idx = int(choice) - 1
            if 0 <= idx < len(sorted_matches):
                player_name = sorted_matches[idx][0]
                ui.console.print(f"[green]Selected: {player_name}[/green]")
            else:
                ui.console.print("[red]Invalid choice.[/red]")
                return False
        except ValueError:
            ui.console.print("[red]Invalid choice.[/red]")
            return False

    config["player_name"] = player_name

    # Save config
    save_config(config)
    ui.console.print()
    ui.console.print(f"[green]Configuration saved![/green]")
    ui.console.print()

    return True


def ensure_config():
    """Ensure config exists, running setup if needed."""
    if not config_exists():
        if not run_setup_wizard():
            sys.exit(1)


def _start_overlay_server():
    """Start overlay server if enabled and Flask is available.

    Returns tuple of (port, startup_message) where port is None if server didn't start.
    The startup_message should be displayed after the table refresh.
    """
    if not is_server_enabled():
        return None, None

    try:
        from .server import is_flask_available, start_server_background
    except ImportError:
        return None, None  # Server module not available

    if not is_flask_available():
        msg = "[yellow]Server disabled: Flask not installed[/yellow]\n[dim]  Install with: pip install flask[/dim]"
        return None, msg

    port = get_server_port()
    actual_port, _ = start_server_background(port=port)
    if actual_port:
        msg = f"[dim]Hosting on http://localhost:{actual_port}/[/dim]\n[dim]  └─ Overlay: http://localhost:{actual_port}/overlays/mmr-graph[/dim]"
        return actual_port, msg
    return None, None


def cmd_config(args):
    """Run setup wizard to configure or reconfigure."""
    run_setup_wizard()


def _parse_one(args_tuple):
    """Parse a single replay file. Top-level function for multiprocessing."""
    replay_path_str, player_name = args_tuple
    replay_id = sha1(replay_path_str)
    try:
        data = parse_replay(replay_path_str, player_name)
        return replay_id, data, None
    except Exception as e:
        return replay_id, None, str(e)


def cmd_scan(args):
    """Scan replay folder and add new replays to database."""
    import concurrent.futures
    import os

    ensure_config()
    db.init_db()

    replay_folder = get_replay_folder()
    player_name = get_player_name()

    replays = find_replays(replay_folder)
    if not replays:
        ui.console.print(f"[yellow]No replays found in:[/yellow] {replay_folder}")
        return

    # Filter to only replays that need parsing
    if not args.force:
        replays = [rp for rp in replays if not db.replay_exists(sha1(str(rp)))]
        if not replays:
            ui.console.print(f"[green]All replays already scanned. Total: {db.get_replay_count()}[/green]")
            return

    ui.console.print(f"[cyan]Scanning {len(replays)} replay(s) in:[/cyan] {replay_folder}")
    ui.console.print(f"[cyan]Player:[/cyan] {player_name}")
    ui.console.print()

    new_count = 0
    skipped = 0
    errors = 0
    total = len(replays)

    # Use process pool for true parallelism (sc2reader is CPU-bound)
    max_workers = min(os.cpu_count() or 4, total, 8)
    work_items = [(str(rp), player_name) for rp in replays]

    with concurrent.futures.ProcessPoolExecutor(max_workers=max_workers) as executor:
        futures = {
            executor.submit(_parse_one, item): replays[i]
            for i, item in enumerate(work_items)
        }

        for i, future in enumerate(concurrent.futures.as_completed(futures), 1):
            replay_path = futures[future]
            ui.show_scan_progress(i, total, replay_path.name)

            replay_id, data, error = future.result()
            if error:
                errors += 1
                if args.verbose:
                    ui.console.print(f"\n[red]Error parsing {replay_path.name}:[/red] {error}")
            elif data:
                db.insert_replay(data)
                new_count += 1
            else:
                skipped += 1

    # Detect unranked games after scan
    if new_count > 0:
        unranked = db.detect_unranked_games()
        if unranked > 0:
            ui.console.print(f"[dim]Detected {unranked} likely unranked game(s)[/dim]")

    ui.show_scan_complete(new_count, db.get_replay_count())

    if errors > 0:
        ui.console.print(f"[yellow]Skipped {errors} replay(s) due to errors[/yellow]")
    if skipped > 0 and args.verbose:
        ui.console.print(f"[dim]Skipped {skipped} already-parsed or non-matching replay(s)[/dim]")


def cmd_show(args):
    """Show replays with optional filtering."""
    ensure_config()
    db.init_db()

    replays = db.get_replays(
        matchup=args.matchup,
        result=args.result,
        map_name=args.map,
        days=args.days,
        limit=args.last or 20,
    )

    ui.show_replays_table(replays)
    ui.show_summary_row(replays)


def cmd_latest(args):
    """Show detailed stats for the most recent game."""
    ensure_config()
    db.init_db()

    replay = db.get_latest_replay()
    ui.show_latest_game(replay)


def cmd_stats(args):
    """Show aggregate statistics."""
    ensure_config()
    db.init_db()

    stats = db.get_stats(matchup=args.matchup, days=args.days)
    matchup_stats = db.get_stats_by_matchup(days=args.days) if not args.matchup else []

    ui.show_stats(stats, matchup_stats, days=args.days)


def cmd_export(args):
    """Export replays to CSV."""
    ensure_config()
    db.init_db()

    replays = db.get_replays(
        matchup=args.matchup,
        result=args.result,
        days=args.days,
        limit=args.last,
    )

    if not replays:
        ui.console.print("[yellow]No replays to export.[/yellow]")
        return

    output_path = args.out or "sc2_export.csv"

    with open(output_path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=replays[0].keys())
        writer.writeheader()
        writer.writerows(replays)

    ui.console.print(f"[green]Exported {len(replays)} replay(s) to:[/green] {output_path}")


def cmd_live(args):
    """Run interactive filtering mode."""
    ensure_config()
    db.init_db()
    auto_scan()
    server_port, server_msg = _start_overlay_server()
    ui.run_interactive_mode(server_port=server_port, startup_message=server_msg)


def cmd_tag(args):
    """Add a tag."""
    from datetime import datetime

    ensure_config()
    db.init_db()

    label = args.label
    tag_date = args.date

    # If no date provided, create ongoing tag from today
    if tag_date is None:
        tag_date = datetime.now().strftime("%Y-%m-%d")
        # Check for ongoing tags and notify
        ongoing_tags = db.get_ongoing_tags()
        if ongoing_tags:
            ongoing_labels = [t["label"] for t in ongoing_tags]
            ui.console.print(f"[dim]Note: {len(ongoing_tags)} ongoing tag(s): {', '.join(ongoing_labels)}[/dim]")

        if db.add_tag(tag_date, label):
            color = ui.get_tag_color(label)
            ui.console.print(f"[green]Started ongoing tag:[/green] [{color}]▸[/{color}] {label} (from {tag_date})")
        else:
            ui.console.print(f"[yellow]Tag already exists:[/yellow] {label} from {tag_date}")
        return

    # Validate date format
    if not ui.is_valid_date(tag_date):
        ui.console.print(f"[red]Invalid date format: {tag_date}. Use YYYY-MM-DD[/red]")
        return

    # Single date tag (with end_date = tag_date)
    if db.add_tag(tag_date, label, end_date=tag_date):
        color = ui.get_tag_color(label)
        ui.console.print(f"[green]Added tag:[/green] [{color}]◆[/{color}] {label} on {tag_date}")
    else:
        ui.console.print(f"[yellow]Tag already exists:[/yellow] {label} on {tag_date}")


def cmd_tag_start(args):
    """Start an ongoing tag."""
    from datetime import datetime

    ensure_config()
    db.init_db()

    label = args.label
    tag_date = args.date or datetime.now().strftime("%Y-%m-%d")

    # Validate date format
    if args.date and not ui.is_valid_date(tag_date):
        ui.console.print(f"[red]Invalid date format: {tag_date}. Use YYYY-MM-DD[/red]")
        return

    # Check for ongoing tags and notify
    ongoing_tags = db.get_ongoing_tags()
    if ongoing_tags:
        ongoing_labels = [t["label"] for t in ongoing_tags]
        ui.console.print(f"[dim]Note: {len(ongoing_tags)} ongoing tag(s): {', '.join(ongoing_labels)}[/dim]")

    if db.add_tag(tag_date, label):
        color = ui.get_tag_color(label)
        ui.console.print(f"[green]Started ongoing tag:[/green] [{color}]▸[/{color}] {label} (from {tag_date})")
    else:
        ui.console.print(f"[yellow]Tag already exists:[/yellow] {label} from {tag_date}")


def cmd_tag_end(args):
    """End an ongoing tag."""
    from datetime import datetime

    ensure_config()
    db.init_db()

    label = args.label
    end_date = args.date or datetime.now().strftime("%Y-%m-%d")

    # Validate date format
    if args.date and not ui.is_valid_date(end_date):
        ui.console.print(f"[red]Invalid date format: {end_date}. Use YYYY-MM-DD[/red]")
        return

    if db.end_tag(label, end_date):
        color = ui.get_tag_color(label)
        ui.console.print(f"[green]Ended tag:[/green] [{color}]◆─◆[/{color}] {label} (ended {end_date})")
    else:
        ui.console.print(f"[yellow]No ongoing tag found:[/yellow] {label}")


def cmd_tags(args):
    """List all tags."""
    ensure_config()
    db.init_db()
    ui.show_tags()


def cmd_untag(args):
    """Remove tag(s) from a date."""
    ensure_config()
    db.init_db()

    tag_date = args.date
    label = args.label  # May be None

    # Validate date format
    if not ui.is_valid_date(tag_date):
        ui.console.print(f"[red]Invalid date format: {tag_date}. Use YYYY-MM-DD[/red]")
        return

    count = db.remove_tag(tag_date, label)
    if count > 0:
        if label:
            ui.console.print(f"[green]Removed tag:[/green] {label} from {tag_date}")
        else:
            ui.console.print(f"[green]Removed {count} tag(s)[/green] from {tag_date}")
    else:
        ui.console.print(f"[yellow]No matching tags found for {tag_date}[/yellow]")


def cmd_gui(args):
    """Launch web GUI in browser."""
    ensure_config()
    db.init_db()
    auto_scan()
    port, _ = _start_overlay_server()
    if port is None:
        ui.console.print("[red]Cannot start GUI: Flask is required.[/red]")
        ui.console.print("[dim]Install with: pip install flask[/dim]")
        sys.exit(1)

    url = f"http://localhost:{port}/"
    import webbrowser
    webbrowser.open(url)

    ui.console.print(f"[bold cyan]SC2 Replay Analyzer GUI[/bold cyan]")
    ui.console.print(f"[dim]Running at {url}[/dim]")
    ui.console.print(f"[dim]Press Ctrl+C to stop[/dim]")

    try:
        import signal
        signal.pause()  # Unix - blocks until signal received
    except AttributeError:
        import time
        while True:
            time.sleep(1)  # Windows fallback


def cmd_columns(args):
    """Manage display columns."""
    ensure_config()

    current_columns = get_display_columns()

    if args.action == "list" or args.action is None:
        # Show available columns with current selection marked
        ui.console.print()
        ui.console.print("[bold cyan]Available columns:[/bold cyan]")
        ui.console.print("[dim](* = currently shown)[/dim]")
        ui.console.print()

        for key, (header, width, justify) in AVAILABLE_COLUMNS.items():
            marker = "[green]*[/green]" if key in current_columns else " "
            ui.console.print(f"  {marker} [bold]{key:15}[/bold] {header:10}")

        ui.console.print()
        ui.console.print(f"[dim]Current: {', '.join(current_columns)}[/dim]")

    elif args.action == "add":
        if not args.columns:
            ui.console.print("[red]Usage: sc2 columns add <column> [column...][/red]")
            return
        added = add_display_columns(args.columns)
        if added:
            ui.console.print(f"[green]Added:[/green] {', '.join(added)}")
        else:
            ui.console.print("[yellow]No columns added (already present or invalid)[/yellow]")

    elif args.action == "remove":
        if not args.columns:
            ui.console.print("[red]Usage: sc2 columns remove <column> [column...][/red]")
            return
        removed = remove_display_columns(args.columns)
        if removed:
            ui.console.print(f"[green]Removed:[/green] {', '.join(removed)}")
        else:
            ui.console.print("[yellow]No columns removed (not present)[/yellow]")

    elif args.action == "set":
        if not args.columns:
            ui.console.print("[red]Usage: sc2 columns set <column> [column...][/red]")
            return
        # Validate all columns
        invalid = [c for c in args.columns if c not in AVAILABLE_COLUMNS]
        if invalid:
            ui.console.print(f"[red]Invalid columns:[/red] {', '.join(invalid)}")
            ui.console.print(f"[dim]Available: {', '.join(AVAILABLE_COLUMNS.keys())}[/dim]")
            return
        set_display_columns(args.columns)
        ui.console.print(f"[green]Columns set to:[/green] {', '.join(args.columns)}")

    elif args.action == "reset":
        reset_display_columns()
        ui.console.print("[green]Columns reset to defaults[/green]")


def main():
    parser = argparse.ArgumentParser(
        description="SC2 Replay Analyzer - Track your StarCraft II progress",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # config command
    config_parser = subparsers.add_parser("config", help="Configure or reconfigure settings")
    config_parser.set_defaults(func=cmd_config)

    # scan command
    scan_parser = subparsers.add_parser("scan", help="Scan replay folder for new games")
    scan_parser.add_argument("--force", action="store_true", help="Re-parse all replays")
    scan_parser.add_argument("--verbose", "-v", action="store_true", help="Show detailed output")
    scan_parser.set_defaults(func=cmd_scan)

    # show command
    show_parser = subparsers.add_parser("show", help="Show recent games")
    show_parser.add_argument("--matchup", "-m", help="Filter by matchup (TvZ, TvP, TvT)")
    show_parser.add_argument("--result", "-r", help="Filter by result (win, loss)")
    show_parser.add_argument("--map", help="Filter by map name (partial match)")
    show_parser.add_argument("--days", "-d", type=int, help="Only show games from last N days")
    show_parser.add_argument("--last", "-l", type=int, help="Show last N games (default: 20)")
    show_parser.set_defaults(func=cmd_show)

    # latest command
    latest_parser = subparsers.add_parser("latest", help="Show stats for most recent game")
    latest_parser.set_defaults(func=cmd_latest)

    # stats command
    stats_parser = subparsers.add_parser("stats", help="Show aggregate statistics")
    stats_parser.add_argument("--matchup", "-m", help="Filter by matchup (TvZ, TvP, TvT)")
    stats_parser.add_argument("--days", "-d", type=int, help="Only include games from last N days")
    stats_parser.set_defaults(func=cmd_stats)

    # export command
    export_parser = subparsers.add_parser("export", help="Export replays to CSV")
    export_parser.add_argument("--out", "-o", help="Output file path (default: sc2_export.csv)")
    export_parser.add_argument("--matchup", "-m", help="Filter by matchup")
    export_parser.add_argument("--result", "-r", help="Filter by result")
    export_parser.add_argument("--days", "-d", type=int, help="Only include games from last N days")
    export_parser.add_argument("--last", "-l", type=int, help="Export last N games")
    export_parser.set_defaults(func=cmd_export)

    # live command
    live_parser = subparsers.add_parser("live", help="Interactive filtering mode")
    live_parser.set_defaults(func=cmd_live)

    # gui command
    gui_parser = subparsers.add_parser("gui", help="Launch web GUI in browser")
    gui_parser.set_defaults(func=cmd_gui)

    # columns command
    columns_parser = subparsers.add_parser("columns", help="Manage display columns")
    columns_parser.add_argument(
        "action",
        nargs="?",
        choices=["list", "add", "remove", "set", "reset"],
        default="list",
        help="Action to perform (default: list)",
    )
    columns_parser.add_argument(
        "columns",
        nargs="*",
        help="Column names (for add/remove/set)",
    )
    columns_parser.set_defaults(func=cmd_columns)

    # tag command - flexible: tag "label" (ongoing) or tag <date> "label" (single)
    tag_parser = subparsers.add_parser("tag", help="Add a tag (ongoing if no date)")
    tag_parser.add_argument("date_or_label", help="Date (YYYY-MM-DD) or label if no date")
    tag_parser.add_argument("label", nargs="?", help="Tag label (if date provided)")
    tag_parser.set_defaults(func=lambda args: cmd_tag(
        argparse.Namespace(
            date=args.date_or_label if args.label else None,
            label=args.label or args.date_or_label
        )
    ))

    # tag-start command
    tag_start_parser = subparsers.add_parser("tag-start", help="Start an ongoing tag")
    tag_start_parser.add_argument("label", help="Tag label")
    tag_start_parser.add_argument("--date", "-d", help="Start date (default: today)")
    tag_start_parser.set_defaults(func=cmd_tag_start)

    # tag-end command
    tag_end_parser = subparsers.add_parser("tag-end", help="End an ongoing tag")
    tag_end_parser.add_argument("label", help="Tag label to end")
    tag_end_parser.add_argument("--date", "-d", help="End date (default: today)")
    tag_end_parser.set_defaults(func=cmd_tag_end)

    # tags command
    tags_parser = subparsers.add_parser("tags", help="List all tags")
    tags_parser.set_defaults(func=cmd_tags)

    # untag command
    untag_parser = subparsers.add_parser("untag", help="Remove tag(s) from a date")
    untag_parser.add_argument("date", help="Date in YYYY-MM-DD format")
    untag_parser.add_argument("label", nargs="?", help="Specific tag label (optional)")
    untag_parser.set_defaults(func=cmd_untag)

    args = parser.parse_args()

    # Default behavior: auto-scan and launch live mode
    if not args.command:
        if not config_exists():
            run_setup_wizard()
        ensure_config()
        db.init_db()
        auto_scan()
        server_port, server_msg = _start_overlay_server()
        ui.run_interactive_mode(server_port=server_port, startup_message=server_msg)
        return

    args.func(args)


if __name__ == "__main__":
    main()
