"""
ID Wispera CLI
Command-line interface for credential governance
"""

import os
import sys
from getpass import getpass
from typing import Optional

import click
from rich.console import Console
from rich.table import Table

from id_wispera import __version__
from id_wispera.vault import create_vault, unlock_vault, vault_exists, get_default_vault_path
from id_wispera.passport import list_passports, get_passport, revoke_passport, create_passport
from id_wispera.detection import detect_credentials, might_contain_credentials
from id_wispera.types import (
    PassportStatus,
    CredentialType,
    Platform,
    VisaType,
    CreatePassportInput,
)


console = Console()


@click.group()
@click.version_option(version=__version__)
def main():
    """ID Wispera - Credential governance for AI agents"""
    pass


@main.command()
@click.option("--path", "-p", default=None, help="Custom vault path")
def init(path: Optional[str]):
    """Initialize a new credential vault"""
    vault_path = path or get_default_vault_path()

    if vault_exists(vault_path):
        console.print("[red]✗[/red] Vault already exists at this location")
        sys.exit(1)

    console.print("[bold]ID Wispera Vault Setup[/bold]\n")

    # Get passphrase
    passphrase = getpass("Create vault passphrase (min 8 chars): ")
    if len(passphrase) < 8:
        console.print("[red]✗[/red] Passphrase must be at least 8 characters")
        sys.exit(1)

    confirm = getpass("Confirm passphrase: ")
    if passphrase != confirm:
        console.print("[red]✗[/red] Passphrases do not match")
        sys.exit(1)

    try:
        create_vault(passphrase, vault_path)
        console.print(f"[green]✓[/green] Vault created at {vault_path}")
    except Exception as e:
        console.print(f"[red]✗[/red] Failed to create vault: {e}")
        sys.exit(1)


@main.command("list")
@click.option("--status", "-s", type=click.Choice(["active", "expiring", "expired", "revoked"]))
@click.option("--tag", "-t", multiple=True, help="Filter by tag")
@click.option("--search", help="Search in name and notes")
@click.option("--path", "-p", default=None, help="Custom vault path")
def list_cmd(status: Optional[str], tag: tuple, search: Optional[str], path: Optional[str]):
    """List passports in the vault"""
    vault_path = path or get_default_vault_path()

    if not vault_exists(vault_path):
        console.print("[red]✗[/red] Vault not found. Run `idw-py init` first.")
        sys.exit(1)

    passphrase = getpass("Vault passphrase: ")

    try:
        vault = unlock_vault(passphrase, vault_path)
    except ValueError:
        console.print("[red]✗[/red] Invalid passphrase")
        sys.exit(1)

    status_filter = PassportStatus(status) if status else None
    tags = list(tag) if tag else None

    passports = list_passports(vault, status=status_filter, tags=tags, search=search)

    if not passports:
        console.print("[dim]No passports found.[/dim]")
        return

    table = Table(title="Passports")
    table.add_column("ID", style="dim")
    table.add_column("Name")
    table.add_column("Type")
    table.add_column("Status")
    table.add_column("Platform")

    for p in passports:
        status_color = {
            "active": "green",
            "expiring": "yellow",
            "expired": "red",
            "revoked": "dim",
            "suspended": "magenta",
        }.get(p.status.value if hasattr(p.status, 'value') else p.status, "white")

        table.add_row(
            p.id[:8] + "...",
            p.name,
            p.credential_type.value if hasattr(p.credential_type, 'value') else str(p.credential_type),
            f"[{status_color}]{p.status.value if hasattr(p.status, 'value') else p.status}[/{status_color}]",
            p.platforms[0].value if p.platforms and hasattr(p.platforms[0], 'value') else str(p.platforms[0]) if p.platforms else "",
        )

    console.print(table)


@main.command()
@click.argument("passport_id")
@click.option("--reveal", is_flag=True, help="Show credential value")
@click.option("--path", "-p", default=None, help="Custom vault path")
def show(passport_id: str, reveal: bool, path: Optional[str]):
    """Show passport details"""
    vault_path = path or get_default_vault_path()

    if not vault_exists(vault_path):
        console.print("[red]✗[/red] Vault not found. Run `idw-py init` first.")
        sys.exit(1)

    passphrase = getpass("Vault passphrase: ")

    try:
        vault = unlock_vault(passphrase, vault_path)
    except ValueError:
        console.print("[red]✗[/red] Invalid passphrase")
        sys.exit(1)

    try:
        passport = get_passport(vault, passport_id)
    except ValueError:
        console.print(f"[red]✗[/red] Passport not found: {passport_id}")
        sys.exit(1)

    console.print(f"\n[bold]PASSPORT: {passport.name}[/bold]")
    console.print(f"[dim]ID: {passport.id}[/dim]\n")
    console.print(f"Type: {passport.credential_type}")
    console.print(f"Visa: {passport.visa_type}")
    console.print(f"Status: {passport.status}")
    console.print(f"Platforms: {', '.join(str(p) for p in passport.platforms)}")
    console.print(f"Owner: {passport.human_owner}")

    if reveal:
        console.print(f"\n[bold]Credential:[/bold]\n{passport.credential_value}")
    else:
        masked = passport.credential_value[:4] + "*" * 20 + passport.credential_value[-4:]
        console.print(f"\nCredential: [dim]{masked}[/dim]")


@main.command()
@click.argument("scan_path", default=".")
@click.option("--system", "-s", is_flag=True, help="Scan known credential locations")
@click.option("--verbose", "-v", is_flag=True, help="Show all files scanned")
def scan(scan_path: str, system: bool, verbose: bool):
    """Scan for exposed credentials"""
    from pathlib import Path

    console.print("\n[bold]Scanning for Credentials[/bold]")
    console.print("─" * 30)

    if system:
        # Scan known locations
        locations = [
            Path.home() / ".aws",
            Path.home() / ".ssh",
            Path.home() / ".openclaw",
            Path.home() / ".docker",
            Path.home() / ".kube",
        ]
        console.print("[dim]Scanning known credential locations...[/dim]\n")
    else:
        locations = [Path(scan_path).resolve()]
        console.print(f"[dim]Path: {locations[0]}[/dim]\n")

    total_credentials = 0
    files_scanned = 0

    skip_dirs = {"node_modules", ".git", "__pycache__", ".venv", "venv", "dist", "build"}
    skip_extensions = {".png", ".jpg", ".gif", ".ico", ".pdf", ".zip", ".tar", ".gz"}

    for location in locations:
        if not location.exists():
            continue

        for file_path in location.rglob("*"):
            if file_path.is_dir():
                continue

            if any(skip in file_path.parts for skip in skip_dirs):
                continue

            if file_path.suffix.lower() in skip_extensions:
                continue

            try:
                content = file_path.read_text(errors="ignore")

                if not might_contain_credentials(content):
                    continue

                results = detect_credentials(content)
                files_scanned += 1

                if results:
                    console.print(f"[yellow]⚠[/yellow] {file_path}")
                    for r in results:
                        console.print(f"  [cyan]•[/cyan] {r.pattern}: line {r.line}")
                        total_credentials += 1
                elif verbose:
                    console.print(f"[dim]✓ {file_path}[/dim]")

            except Exception:
                pass

    console.print()
    if total_credentials > 0:
        console.print(f"[yellow]⚠[/yellow] Found {total_credentials} potential credential(s) in {files_scanned} files")
    else:
        console.print(f"[green]✓[/green] No credentials detected (scanned {files_scanned} files)")


@main.command()
@click.argument("passport_id")
@click.option("--reason", "-r", required=True, help="Revocation reason")
@click.option("--path", "-p", default=None, help="Custom vault path")
def revoke(passport_id: str, reason: str, path: Optional[str]):
    """Revoke a passport"""
    vault_path = path or get_default_vault_path()

    if not vault_exists(vault_path):
        console.print("[red]✗[/red] Vault not found. Run `idw-py init` first.")
        sys.exit(1)

    passphrase = getpass("Vault passphrase: ")

    try:
        vault = unlock_vault(passphrase, vault_path)
    except ValueError:
        console.print("[red]✗[/red] Invalid passphrase")
        sys.exit(1)

    actor = os.environ.get("USER", "cli-user")

    try:
        passport = revoke_passport(vault, passport_id, reason=reason, actor=actor)
        console.print(f"[green]✓[/green] Revoked passport: {passport.name}")
    except ValueError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)


def _guess_platform(name: str, value: str) -> str:
    """Guess the platform from credential name/value patterns"""
    combined = (name + " " + value).lower()

    if "sk-ant-" in value or "anthropic" in combined:
        return "anthropic"
    if value.startswith("sk-") or "openai" in combined:
        return "openai"
    if value.startswith(("ghp_", "gho_", "github_pat_")) or "github" in combined:
        return "github"
    if value.startswith("AKIA") or "aws" in combined:
        return "aws"
    if "azure" in combined:
        return "azure-ai"
    if "google" in combined:
        return "google-a2a"

    return "custom"


def _mask_credential(value: str) -> str:
    """Mask a credential value for display"""
    if len(value) <= 8:
        return "*" * len(value)
    return value[:4] + "*" * min(len(value) - 8, 20) + value[-4:]


def _confidence_tag(confidence: float) -> str:
    """Get a confidence level tag"""
    if confidence >= 0.95:
        return "high-confidence"
    elif confidence >= 0.8:
        return "medium-confidence"
    return "low-confidence"


def _scan_path_for_credentials(scan_target: str, min_confidence: float = 0.0):
    """Scan a file or directory for credentials, returning (filepath, results) tuples"""
    from pathlib import Path

    target = Path(scan_target).resolve()

    skip_dirs = {".git", "node_modules", "__pycache__", ".venv", "venv", "vendor", "dist", "build"}
    skip_extensions = {
        ".png", ".jpg", ".jpeg", ".gif", ".ico", ".pdf", ".zip", ".tar", ".gz",
        ".bz2", ".7z", ".exe", ".dll", ".so", ".dylib", ".bin", ".woff", ".woff2",
        ".ttf", ".eot", ".mp3", ".mp4", ".avi", ".mov",
    }
    max_file_size = 1_000_000  # 1MB

    results = []

    if target.is_file():
        files = [target]
    else:
        files = list(target.rglob("*"))

    for file_path in files:
        if file_path.is_dir():
            continue

        if any(skip in file_path.parts for skip in skip_dirs):
            continue

        if file_path.suffix.lower() in skip_extensions:
            continue

        try:
            if file_path.stat().st_size > max_file_size:
                continue
        except OSError:
            continue

        try:
            content = file_path.read_text(errors="ignore")

            if not might_contain_credentials(content):
                continue

            detections = detect_credentials(content)

            if min_confidence > 0:
                detections = [d for d in detections if d.confidence >= min_confidence]

            if detections:
                results.append((str(file_path), detections))
        except Exception:
            continue

    return results


@main.command("import")
@click.argument("path", default=".")
@click.option("--all", "import_all", is_flag=True, help="Scan and import all detected credentials")
@click.option("--min-confidence", type=float, default=None, help="Minimum confidence threshold (0.0-1.0)")
@click.option("--owner", default=None, help="Owner email for imported passports")
@click.option("--yes", "-y", is_flag=True, help="Auto-import without confirmation")
@click.option("--path", "-p", "vault_path", default=None, help="Custom vault path")
def import_cmd(path: str, import_all: bool, min_confidence: Optional[float], owner: Optional[str], yes: bool, vault_path: Optional[str]):
    """Import detected credentials as passports"""
    from pathlib import Path

    target = Path(path).resolve()

    if not target.exists():
        console.print(f"[red]✗[/red] Path not found: {target}")
        sys.exit(1)

    # Determine scan mode
    is_scan_mode = import_all or min_confidence is not None

    if not is_scan_mode and target.is_dir():
        console.print("[red]✗[/red] Specify a file for single-file import, or use --all / --min-confidence for directory scan")
        sys.exit(1)

    min_conf = 0.0
    if min_confidence is not None:
        min_conf = min_confidence
    elif import_all:
        min_conf = 0.0

    # Scan for credentials
    console.print("\n[bold]Scanning for Credentials[/bold]")
    console.print("─" * 30)
    console.print(f"[dim]Target: {target}[/dim]")

    if is_scan_mode:
        console.print(f"[dim]Mode: {'scan all' if import_all else f'min confidence {min_conf}'}[/dim]\n")
        scan_results = _scan_path_for_credentials(str(target), min_conf)
    else:
        # Single file mode
        console.print("[dim]Mode: single file[/dim]\n")
        try:
            content = target.read_text(errors="ignore")
            detections = detect_credentials(content)
            if detections:
                scan_results = [(str(target), detections)]
            else:
                scan_results = []
        except Exception as e:
            console.print(f"[red]✗[/red] Failed to read file: {e}")
            sys.exit(1)

    # Flatten results for display
    all_detections = []
    for filepath, detections in scan_results:
        for d in detections:
            all_detections.append((filepath, d))

    if not all_detections:
        console.print("[green]✓[/green] No credentials detected")
        return

    # Display found credentials
    table = Table(title=f"Found {len(all_detections)} Credential(s)")
    table.add_column("#", style="dim")
    table.add_column("File")
    table.add_column("Line")
    table.add_column("Pattern")
    table.add_column("Confidence")
    table.add_column("Value (masked)")

    for i, (filepath, d) in enumerate(all_detections, 1):
        filename = Path(filepath).name
        conf_color = "green" if d.confidence >= 0.9 else "yellow" if d.confidence >= 0.7 else "red"
        table.add_row(
            str(i),
            filename,
            str(d.line or "?"),
            d.pattern or "unknown",
            f"[{conf_color}]{d.confidence:.2f}[/{conf_color}]",
            _mask_credential(d.value),
        )

    console.print(table)
    console.print()

    # Confirmation
    if not yes:
        if not click.confirm(f"Import {len(all_detections)} credential(s) as passports?"):
            console.print("[dim]Import cancelled.[/dim]")
            return

    # Get owner
    if not owner:
        owner = click.prompt("Owner email")

    # Unlock vault
    vpath = vault_path or get_default_vault_path()

    if not vault_exists(vpath):
        console.print("[red]✗[/red] Vault not found. Run `idw-py init` first.")
        sys.exit(1)

    passphrase = getpass("Vault passphrase: ")

    try:
        vault = unlock_vault(passphrase, vpath)
    except ValueError:
        console.print("[red]✗[/red] Invalid passphrase")
        sys.exit(1)

    # Import credentials as passports
    imported = 0
    for filepath, d in all_detections:
        filename = Path(filepath).name
        pattern_name = d.pattern or "Unknown pattern"
        platform = _guess_platform(pattern_name, d.value)

        try:
            passport_input = CreatePassportInput(
                name=f"{pattern_name} in {filename}",
                credential_type=d.type,
                credential_value=d.value,
                visa_type=VisaType.ACCESS,
                platforms=[Platform(platform)],
                human_owner=owner,
                tags=[
                    "imported",
                    "scan",
                    _confidence_tag(d.confidence),
                    f"file:{filename}",
                ],
                notes=f"Detected in {filepath} at line {d.line}. Confidence: {d.confidence}. Pattern: {pattern_name}.",
            )

            passport = create_passport(vault, passport_input)
            imported += 1
            console.print(f"[green]✓[/green] Imported: {passport.name} ({passport.id[:8]}...)")
        except Exception as e:
            console.print(f"[red]✗[/red] Failed to import {pattern_name} in {filename}: {e}")

    console.print(f"\n[bold]Imported {imported}/{len(all_detections)} credential(s) as passports[/bold]")


if __name__ == "__main__":
    main()
