"""
BiasClear CLI — Terminal Interface

Usage:
    biasclear scan "Everyone agrees this is settled law." --domain legal
    biasclear scan --file brief.txt --domain legal
    biasclear patterns --domain legal
    biasclear audit --limit 10
    biasclear migrate --encrypt

Installed via: pip install biasclear[cli]
"""

from __future__ import annotations

import argparse
import json
import sys
import os
from typing import Optional


# ── Colors ───────────────────────────────────────────────────────────

class _C:
    """ANSI color codes. Disabled when NO_COLOR is set or not a TTY."""
    _on = sys.stdout.isatty() and not os.environ.get("NO_COLOR")

    RED = "\033[31m" if _on else ""
    GREEN = "\033[32m" if _on else ""
    YELLOW = "\033[33m" if _on else ""
    BLUE = "\033[34m" if _on else ""
    MAGENTA = "\033[35m" if _on else ""
    CYAN = "\033[36m" if _on else ""
    BOLD = "\033[1m" if _on else ""
    DIM = "\033[2m" if _on else ""
    RESET = "\033[0m" if _on else ""


def _severity_color(severity: str) -> str:
    return {
        "critical": _C.RED,
        "high": _C.RED,
        "medium": _C.YELLOW,
        "low": _C.GREEN,
    }.get(severity, _C.DIM)


# ── Commands ─────────────────────────────────────────────────────────

def cmd_scan(args: argparse.Namespace) -> int:
    """Scan text for PIT distortions."""
    import asyncio
    from biasclear.detector import scan_local
    from biasclear.patterns.learned import LearningRing

    text = _get_text(args)
    if not text:
        print(f"{_C.RED}Error: provide text via argument or --file{_C.RESET}", file=sys.stderr)
        return 1

    ring = LearningRing()
    external = ring.get_active_patterns()
    result = asyncio.run(scan_local(text, domain=args.domain, external_patterns=external))

    score = result.get("truth_score", 0)
    detected = result.get("bias_detected", False)
    flags = result.get("flags", [])

    # Header
    score_color = _C.GREEN if score >= 80 else (_C.YELLOW if score >= 50 else _C.RED)
    print(f"\n{_C.BOLD}BiasClear Scan{_C.RESET}")
    print(f"{'─' * 50}")
    print(f"  Truth Score:   {score_color}{_C.BOLD}{score}/100{_C.RESET}")
    print(f"  Bias Detected: {_C.RED if detected else _C.GREEN}{'YES' if detected else 'NO'}{_C.RESET}")
    print(f"  Domain:        {args.domain}")
    print(f"  PIT Tier:      {result.get('pit_tier', 'none')}")
    print(f"  Severity:      {result.get('severity', 'none')}")

    if flags:
        print(f"\n{_C.BOLD}Flags ({len(flags)}){_C.RESET}")
        print(f"{'─' * 50}")
        for i, flag in enumerate(flags, 1):
            sev = flag.get("severity", "")
            sc = _severity_color(sev)
            print(f"  {_C.DIM}{i}.{_C.RESET} {sc}{_C.BOLD}[{sev.upper()}]{_C.RESET} {flag.get('pattern_id', '')}")
            print(f"     {_C.DIM}matched:{_C.RESET} \"{flag.get('matched_text', '')}\"")
            if flag.get("principle"):
                print(f"     {_C.DIM}principle:{_C.RESET} {flag['principle']}")
    else:
        print(f"\n  {_C.GREEN}✓ No distortions detected.{_C.RESET}")

    print()

    if args.json:
        print(json.dumps(result, indent=2, default=str))

    return 0 if not detected else 2  # Exit code 2 = bias detected


def cmd_patterns(args: argparse.Namespace) -> int:
    """Show active detection patterns."""
    from biasclear.frozen_core import STRUCTURAL_PATTERNS, CORE_VERSION
    from biasclear.patterns.learned import LearningRing

    ring = LearningRing()
    stats = ring.get_stats()

    # All frozen patterns (not domain-filtered — patterns don't have domain attr)
    all_patterns = STRUCTURAL_PATTERNS

    print(f"\n{_C.BOLD}BiasClear Patterns{_C.RESET} {_C.DIM}v{CORE_VERSION}{_C.RESET}")
    print(f"{'─' * 60}")
    print(f"  Domain:          {args.domain}")
    print(f"  Frozen patterns: {_C.CYAN}{len(all_patterns)}{_C.RESET}")
    print(f"  Learned active:  {_C.CYAN}{stats.get('active', 0)}{_C.RESET}")
    print(f"  Learned staging: {_C.DIM}{stats.get('staging', 0)}{_C.RESET}")

    if args.verbose:
        print(f"\n{_C.BOLD}Frozen Patterns{_C.RESET}")
        print(f"{'─' * 60}")
        for p in all_patterns:
            sc = _severity_color(p.severity)
            print(f"  {sc}[{p.severity.upper():>8}]{_C.RESET} {p.id}")
            print(f"  {_C.DIM}         {p.description}{_C.RESET}")

    print()
    return 0


def cmd_audit(args: argparse.Namespace) -> int:
    """Show audit chain entries."""
    from biasclear.audit import AuditChain

    chain = AuditChain()
    entries = chain.get_recent(limit=args.limit, event_type=args.type)
    integrity = chain.verify_chain(limit=100)

    print(f"\n{_C.BOLD}BiasClear Audit Chain{_C.RESET}")
    print(f"{'─' * 60}")
    verified = integrity.get("verified", False)
    v_color = _C.GREEN if verified else _C.RED
    print(f"  Chain Integrity: {v_color}{'VERIFIED' if verified else 'BROKEN'}{_C.RESET}")
    print(f"  Total entries:   {chain.get_count()}")
    print(f"  Showing:         {len(entries)}")

    if entries:
        print(f"\n{_C.BOLD}Recent Entries{_C.RESET}")
        print(f"{'─' * 60}")
        for e in entries:
            ts = e.get("timestamp", "")[:19]
            print(f"  {_C.DIM}{ts}{_C.RESET}  {_C.CYAN}{e.get('event_type', ''):<20}{_C.RESET}  {_C.DIM}{e.get('hash', '')[:12]}…{_C.RESET}")

    if args.json:
        print(json.dumps({"entries": entries, "integrity": integrity}, indent=2, default=str))

    print()
    return 0


def cmd_migrate(args: argparse.Namespace) -> int:
    """Migrate plaintext databases to encrypted."""
    import sqlite3
    import tempfile
    import shutil

    db_key = os.environ.get("BIASCLEAR_DB_KEY")
    if not db_key:
        print(f"{_C.RED}Error: BIASCLEAR_DB_KEY must be set for migration.{_C.RESET}", file=sys.stderr)
        return 1

    try:
        import sqlcipher3
    except ImportError:
        print(f"{_C.RED}Error: sqlcipher3 not installed. Run: pip install sqlcipher3{_C.RESET}", file=sys.stderr)
        return 1

    db_files = args.files if args.files else ["biasclear_audit.db", "biasclear_patterns.db"]
    migrated = 0

    for db_path in db_files:
        if not os.path.exists(db_path):
            print(f"  {_C.DIM}skip:{_C.RESET} {db_path} (not found)")
            continue

        # Check if already encrypted
        try:
            test_conn = sqlite3.connect(db_path)
            test_conn.execute("SELECT COUNT(*) FROM sqlite_master")
            test_conn.close()
        except Exception:
            print(f"  {_C.YELLOW}skip:{_C.RESET} {db_path} (already encrypted or corrupt)")
            continue

        print(f"  {_C.CYAN}migrating:{_C.RESET} {db_path}")

        # Read all data from plaintext
        src = sqlite3.connect(db_path)
        # Create encrypted copy
        tmp_path = db_path + ".enc.tmp"
        dst = sqlcipher3.connect(tmp_path)
        dst.execute(f"PRAGMA key='{db_key}'")

        # Copy schema + data via SQL dump
        for line in src.iterdump():
            dst.execute(line)
        dst.commit()
        dst.close()
        src.close()

        # Swap files
        backup_path = db_path + ".plaintext.bak"
        shutil.move(db_path, backup_path)
        shutil.move(tmp_path, db_path)

        print(f"  {_C.GREEN}done:{_C.RESET} {db_path} (backup: {backup_path})")
        migrated += 1

    print(f"\n  {_C.BOLD}Migrated {migrated} database(s).{_C.RESET}\n")
    return 0


# ── Helpers ──────────────────────────────────────────────────────────

def _get_text(args: argparse.Namespace) -> Optional[str]:
    """Get text from argument, --file, or stdin pipe."""
    if args.text:
        return args.text
    if args.file:
        with open(args.file, "r") as f:
            return f.read()
    if not sys.stdin.isatty():
        return sys.stdin.read()
    return None


# ── Main ─────────────────────────────────────────────────────────────

def main() -> int:
    parser = argparse.ArgumentParser(
        prog="biasclear",
        description="BiasClear — Detect and correct structural bias in text.",
    )
    parser.add_argument("--version", action="store_true", help="Show version")
    sub = parser.add_subparsers(dest="command")

    # scan
    p_scan = sub.add_parser("scan", help="Scan text for PIT distortions")
    p_scan.add_argument("text", nargs="?", help="Text to scan")
    p_scan.add_argument("--file", "-f", help="Read text from file")
    p_scan.add_argument("--domain", "-d", default="general", help="Domain (general, legal, media, financial)")
    p_scan.add_argument("--json", "-j", action="store_true", help="Output raw JSON")

    # patterns
    p_pat = sub.add_parser("patterns", help="Show detection patterns")
    p_pat.add_argument("--domain", "-d", default="general", help="Domain filter")
    p_pat.add_argument("--verbose", "-v", action="store_true", help="Show all pattern details")

    # audit
    p_aud = sub.add_parser("audit", help="Show audit chain")
    p_aud.add_argument("--limit", "-n", type=int, default=10, help="Max entries")
    p_aud.add_argument("--type", "-t", help="Filter by event type")
    p_aud.add_argument("--json", "-j", action="store_true", help="Output raw JSON")

    # migrate
    p_mig = sub.add_parser("migrate", help="Migrate plaintext databases to encrypted")
    p_mig.add_argument("files", nargs="*", help="Database files to migrate (default: audit + patterns)")

    args = parser.parse_args()

    if args.version:
        from biasclear.frozen_core import CORE_VERSION
        print(f"BiasClear v{CORE_VERSION}")
        return 0

    if not args.command:
        parser.print_help()
        return 0

    handlers = {
        "scan": cmd_scan,
        "patterns": cmd_patterns,
        "audit": cmd_audit,
        "migrate": cmd_migrate,
    }
    return handlers[args.command](args)


def cli_entry() -> None:
    """Entry point for console_scripts."""
    sys.exit(main())


if __name__ == "__main__":
    cli_entry()
