from __future__ import annotations

import argparse
import json
import os
import subprocess
import sys
from datetime import datetime
from pathlib import Path

import httpx
from sqlalchemy import create_engine, text

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

from services.shared.security import issue_app_token


def _load_env_file(path: str) -> None:
    env_path = Path(path).expanduser().resolve()
    if not env_path.exists() or not env_path.is_file():
        return
    for line in env_path.read_text(encoding="utf-8").splitlines():
        stripped = line.strip()
        if not stripped or stripped.startswith("#") or "=" not in stripped:
            continue
        key, value = stripped.split("=", 1)
        key = key.strip()
        if key and key not in os.environ:
            os.environ[key] = value.strip()


def _bool_env(name: str, default: bool) -> bool:
    raw = os.getenv(name)
    if raw is None:
        return default
    return raw.strip().lower() in {"1", "true", "yes", "on"}


def _ops_headers() -> dict[str, str]:
    if _bool_env("ALLOW_LEGACY_HEADER_AUTH", True):
        return {"X-User": "admin", "X-Role": "admin"}
    token = issue_app_token(
        subject="ops:track9-evidence",
        username="track9-evidence",
        role="admin",
        auth_source="service",
        provider="track9-script",
        ttl_seconds=300,
    )
    return {"Authorization": f"Bearer {token}"}


def _get_json(url: str) -> dict | list:
    with httpx.Client(timeout=15, trust_env=False) as client:
        response = client.get(url, headers=_ops_headers())
        response.raise_for_status()
        return response.json()


def _write_json(path: Path, payload: dict | list) -> None:
    path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")


def _query_rows(conn, sql: str, limit: int = 20) -> list[dict]:
    rows = conn.execute(text(sql), {"limit": limit}).mappings().all()
    return [dict(item) for item in rows]


def _run_command(command: list[str], output_path: Path) -> int:
    result = subprocess.run(  # noqa: S603
        command,
        capture_output=True,
        text=True,
        check=False,
    )
    output = (result.stdout or "").strip()
    errors = (result.stderr or "").strip()
    rendered = "\n".join(
        [
            f"$ {' '.join(command)}",
            f"exit_code={result.returncode}",
            "",
            "STDOUT:",
            output if output else "<empty>",
            "",
            "STDERR:",
            errors if errors else "<empty>",
            "",
        ]
    )
    output_path.write_text(rendered, encoding="utf-8")
    return int(result.returncode)


def _render_summary(
    *,
    base_url: str,
    db_url: str,
    out_dir: Path,
    bridge_status: dict,
    failed_events: list[dict],
    started_rows: list[dict],
    ended_rows: list[dict],
    recording_rows: list[dict],
    preflight_exit_code: int | None,
    smoke_exit_code: int | None,
    track9_check_exit_code: int | None,
) -> str:
    now = datetime.now().isoformat(timespec="seconds")
    lines = [
        "# Track 9 Acceptance Summary",
        "",
        f"- Generated at: `{now}`",
        f"- Gateway: `{base_url}`",
        f"- Database: `{db_url}`",
        f"- Evidence directory: `{out_dir.as_posix()}`",
        "",
        "## Bridge status",
        "",
        f"- `status`: `{bridge_status.get('status')}`",
        f"- `ami_connected`: `{bridge_status.get('ami_connected')}`",
        f"- `queue_codes_loaded`: `{len(bridge_status.get('queue_codes_loaded') or [])}`",
        f"- `sftp_enabled`: `{bridge_status.get('sftp_enabled')}`",
        "",
        "## Event evidence counts",
        "",
        f"- `failed bridge events`: `{len(failed_events)}`",
        f"- `call.started (asterisk source)`: `{len(started_rows)}`",
        f"- `call.ended`: `{len(ended_rows)}`",
        f"- `recording-ready with recording_id`: `{len(recording_rows)}`",
        "",
        "## Files",
        "",
        "- `bridge_health.json`",
        "- `bridge_status.json`",
        "- `bridge_failed_events.json`",
        "- `db_voice_call_started.json`",
        "- `db_voice_call_ended.json`",
        "- `db_recording_ready_links.json`",
        "- `playback-proof.md`",
    ]
    if preflight_exit_code is not None:
        lines.extend(
            [
                "- `preflight_output.txt`",
                f"  status: `{'PASS' if preflight_exit_code == 0 else 'FAIL'}`",
            ]
        )
    if smoke_exit_code is not None:
        lines.extend(
            [
                "- `smoke_output.txt`",
                f"  status: `{'PASS' if smoke_exit_code == 0 else 'FAIL'}`",
            ]
        )
    if track9_check_exit_code is not None:
        lines.extend(
            [
                "- `track9_check_output.txt`",
                f"  status: `{'PASS' if track9_check_exit_code == 0 else 'FAIL'}`",
            ]
        )
    lines.extend(
        [
            "",
            "## Manual attachments",
            "",
            "- Add supervisor playback proof (screenshot or note) in `playback-proof.md`.",
            "",
            "## Acceptance decision",
            "",
            "- [ ] GO",
            "- [ ] NO-GO",
            "",
            "## Notes",
            "",
            "- Fill environment details and any deviations from runbook.",
        ]
    )
    return "\n".join(lines) + "\n"


def main() -> int:
    parser = argparse.ArgumentParser(description="Collect Track 9 acceptance evidence package")
    parser.add_argument("--base-url", default="http://127.0.0.1:8080")
    parser.add_argument("--env-file", default=".env.production")
    parser.add_argument("--database-url", required=True)
    parser.add_argument("--out-dir", default="")
    parser.add_argument("--limit", type=int, default=20)
    parser.add_argument("--run-checks", action="store_true")
    parser.add_argument("--require-recording", action="store_true")
    parser.add_argument("--python-exe", default=sys.executable)
    args = parser.parse_args()

    _load_env_file(args.env_file)

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    out_dir = (
        Path(args.out_dir)
        if args.out_dir.strip()
        else Path("docs/acceptance/track9") / timestamp
    )
    out_dir.mkdir(parents=True, exist_ok=True)

    base_url = args.base_url.rstrip("/")
    health = _get_json(f"{base_url}/proxy/asterisk-bridge/health")
    status = _get_json(f"{base_url}/proxy/asterisk-bridge/asterisk/status")
    failed_events = _get_json(f"{base_url}/proxy/asterisk-bridge/asterisk/events?status=failed")

    _write_json(out_dir / "bridge_health.json", health)  # type: ignore[arg-type]
    _write_json(out_dir / "bridge_status.json", status)  # type: ignore[arg-type]
    _write_json(out_dir / "bridge_failed_events.json", failed_events)  # type: ignore[arg-type]

    engine = create_engine(args.database_url, future=True)
    with engine.begin() as conn:
        started_rows = _query_rows(
            conn,
            """
            SELECT event_id, call_id, interaction_id, payload_json, created_at
            FROM voice_events
            WHERE event_type = 'call.started' AND payload_json LIKE '%"source": "asterisk"%'
            ORDER BY id DESC
            LIMIT :limit
            """,
            limit=args.limit,
        )
        ended_rows = _query_rows(
            conn,
            """
            SELECT event_id, call_id, interaction_id, payload_json, created_at
            FROM voice_events
            WHERE event_type = 'call.ended'
            ORDER BY id DESC
            LIMIT :limit
            """,
            limit=args.limit,
        )
        recording_rows = _query_rows(
            conn,
            """
            SELECT bridge_event_id, call_id, interaction_id, recording_id, forward_status, updated_at
            FROM asterisk_event_log
            WHERE ami_event_name = 'MVPCCRecordingReady' AND recording_id IS NOT NULL
            ORDER BY id DESC
            LIMIT :limit
            """,
            limit=args.limit,
        )

    _write_json(out_dir / "db_voice_call_started.json", started_rows)
    _write_json(out_dir / "db_voice_call_ended.json", ended_rows)
    _write_json(out_dir / "db_recording_ready_links.json", recording_rows)
    (out_dir / "playback-proof.md").write_text(
        "\n".join(
            [
                "# Playback Proof",
                "",
                "- Date/time:",
                "- Supervisor user:",
                "- Recording ID:",
                "- Playback URL:",
                "- Result (played/downloaded):",
                "- Evidence link/screenshot path:",
                "",
            ]
        ),
        encoding="utf-8",
    )

    preflight_exit_code: int | None = None
    smoke_exit_code: int | None = None
    track9_check_exit_code: int | None = None
    if args.run_checks:
        preflight_cmd = [
            args.python_exe,
            "scripts/track9_preflight.py",
            "--base-url",
            base_url,
            "--check-sftp",
        ]
        smoke_cmd = [
            args.python_exe,
            "scripts/asterisk_lab_smoke.py",
            "--base-url",
            base_url,
            "--database-url",
            args.database_url,
        ]
        check_cmd = [
            args.python_exe,
            "scripts/track9_check.py",
            "--base-url",
            base_url,
            "--database-url",
            args.database_url,
        ]
        if args.require_recording:
            smoke_cmd.append("--require-recording")
            check_cmd.append("--require-recording")

        preflight_exit_code = _run_command(preflight_cmd, out_dir / "preflight_output.txt")
        smoke_exit_code = _run_command(smoke_cmd, out_dir / "smoke_output.txt")
        track9_check_exit_code = _run_command(check_cmd, out_dir / "track9_check_output.txt")

    summary = _render_summary(
        base_url=base_url,
        db_url=args.database_url,
        out_dir=out_dir,
        bridge_status=status if isinstance(status, dict) else {},
        failed_events=failed_events if isinstance(failed_events, list) else [],
        started_rows=started_rows,
        ended_rows=ended_rows,
        recording_rows=recording_rows,
        preflight_exit_code=preflight_exit_code,
        smoke_exit_code=smoke_exit_code,
        track9_check_exit_code=track9_check_exit_code,
    )
    (out_dir / "track9-acceptance.md").write_text(summary, encoding="utf-8")

    print("[PASS] Track 9 evidence package created")
    print(f"- output_dir: {out_dir.as_posix()}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
