"""
core/orchestrator.py — VaultHound v1.0.0
Main scan pipeline — wires all modules together.
"""

from __future__ import annotations

import json
from pathlib import Path

from rich.panel import Panel
from rich.rule import Rule
from rich.table import Table

from core.patterns import get_patterns
from core.scanner import Scanner
from core.dir_scan import scan_directory
from core.url_scan import scan_url
from core.git_scan import scan_git_history
from output.reports import generate_json_report, generate_html_report, generate_text_report
from utils.helpers import ensure_dir, sanitize_dirname, timestamp
from utils.logger import console, safe_print, log, setup_file_logger
from utils.models import ScanConfig, ScanMode, ScanResult, SecretFinding, Severity

SEVERITY_COLORS = {
    "critical": "bold red",
    "high":     "orange1",
    "medium":   "yellow",
    "low":      "green",
    "info":     "dim",
}


def _severity_badge(sev: str) -> str:
    color = SEVERITY_COLORS.get(sev, "white")
    return f"[{color}]{sev.upper()}[/]"


def render_findings_table(findings: list[SecretFinding]) -> Table:
    table = Table(
        title="[bold]Secrets Found[/]",
        show_lines=True,
        highlight=True,
        border_style="cyan",
    )
    table.add_column("Severity",     justify="center", style="bold")
    table.add_column("Pattern",      style="cyan")
    table.add_column("Match",        style="yellow")
    table.add_column("Source",       max_width=50, overflow="fold")
    table.add_column("Line",         justify="right")
    table.add_column("Context",      max_width=40, overflow="fold", style="dim")

    for f in sorted(findings, key=lambda x: Severity.rank(x.severity), reverse=True):
        table.add_row(
            _severity_badge(f.severity),
            f.pattern_name,
            f.display_match,
            f.source,
            str(f.line_number) if f.line_number else "-",
            f.context[:60] if f.context else "-",
        )
    return table


def orchestrate(cfg: ScanConfig) -> ScanResult:
    # ── Setup output ──────────────────────────────────────────────────────
    stamp      = timestamp()
    name       = sanitize_dirname(cfg.target.replace("https://","").replace("http://",""))
    out_folder = ensure_dir(Path(cfg.output_dir) / f"{name}_{stamp}")
    log_path   = out_folder / "vaulthound.log"
    setup_file_logger(log_path)

    (out_folder / "scan_config.json").write_text(
        json.dumps(cfg.to_dict(), indent=2)
    )

    result = ScanResult(
        target     = cfg.target,
        mode       = cfg.mode.value,
        start_time = stamp,
    )

    console.print(f"\n[success]📁 Output: {out_folder}[/]\n")

    # ── Load patterns ─────────────────────────────────────────────────────
    patterns = get_patterns(cfg.patterns_file)
    scanner  = Scanner(patterns=patterns, min_severity=cfg.min_severity)
    safe_print(f"[info]  Loaded {len(patterns)} detection patterns[/]")
    safe_print(f"[info]  Minimum severity: {cfg.min_severity.upper()}[/]\n")

    # ── URL mode ──────────────────────────────────────────────────────────
    if cfg.mode == ScanMode.URL:
        console.print(Panel.fit("[phase] PHASE 1 — URL Scan [/]"))
        findings, urls = scan_url(
            target_url  = cfg.target,
            scanner     = scanner,
            scan_js     = cfg.scan_js,
            crawl_depth = cfg.crawl_depth,
            threads     = cfg.threads,
            timeout     = cfg.timeout,
        )
        result.findings.extend(findings)
        result.urls_scanned.extend(urls)

    # ── DIR mode ──────────────────────────────────────────────────────────
    elif cfg.mode == ScanMode.DIR:
        console.print(Panel.fit("[phase] PHASE 1 — Directory Scan [/]"))
        findings, files = scan_directory(
            target_dir = cfg.target,
            scanner    = scanner,
            threads    = cfg.threads,
        )
        result.findings.extend(findings)
        result.files_scanned.extend(files)

        # ── Git history ───────────────────────────────────────────────────
        if cfg.scan_git:
            console.print(Panel.fit("[phase] PHASE 2 — Git History Scan [/]"))
            git_findings, commits = scan_git_history(
                target_dir = cfg.target,
                scanner    = scanner,
            )
            result.findings.extend(git_findings)
            result.git_commits_scanned = commits

    # ── Dedup final findings ──────────────────────────────────────────────
    seen: set[tuple] = set()
    deduped: list[SecretFinding] = []
    for f in result.findings:
        key = (f.pattern_name, f.display_match, f.source)
        if key not in seen:
            seen.add(key)
            deduped.append(f)
    result.findings = deduped

    # ── Filter by severity ────────────────────────────────────────────────
    result.findings = result.filter_by_severity(cfg.min_severity)

    result.end_time = timestamp()

    # ── Reports ───────────────────────────────────────────────────────────
    console.print(Rule("[header]Generating Reports[/]"))
    txt_path  = out_folder / "report.txt"
    json_path = out_folder / "report.json"
    html_path = out_folder / "report.html"

    generate_text_report(result, txt_path)
    generate_json_report(result, json_path)
    if not cfg.no_report:
        generate_html_report(result, html_path)

    console.print(f"[info]  TXT:  {txt_path}[/]")
    console.print(f"[info]  JSON: {json_path}[/]")
    if not cfg.no_report:
        console.print(f"[info]  HTML: {html_path}[/]")

    # ── Terminal summary ──────────────────────────────────────────────────
    if result.findings:
        console.print(render_findings_table(result.findings))

    console.print(Panel.fit(
        f"[success]✔ VaultHound Complete[/]\n"
        f"Target: [cyan]{cfg.target}[/]\n"
        f"Secrets Found:  [danger]{result.total}[/]  |  "
        f"Critical: [bold red]{result.critical_count}[/]  |  "
        f"High: [orange1]{result.high_count}[/]  |  "
        f"Medium: [yellow]{result.medium_count}[/]  |  "
        f"Low: [green]{result.low_count}[/]\n"
        f"Files Scanned:  [cyan]{len(result.files_scanned)}[/]  |  "
        f"URLs: [cyan]{len(result.urls_scanned)}[/]  |  "
        f"Git Commits: [cyan]{result.git_commits_scanned}[/]\n"
        f"Reports → [dim]{out_folder}[/]",
        border_style="cyan" if result.total == 0 else "red",
    ))

    if result.errors:
        safe_print(f"[warning]{len(result.errors)} error(s) — see vaulthound.log[/]")

    return result
