"""Source-of-truth quality-gate runner for titan-iac."""

from __future__ import annotations

import argparse
import base64
import json
import os
import subprocess
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path
from typing import Any

from testing.quality_contract import load_contract
from testing.quality_coverage import (
    compute_workspace_line_coverage,
    run_check as run_coverage_check,
)
from testing.quality_docs import run_check as run_docs_check
from testing.quality_hygiene import (
    count_files_over_line_limit,
    count_files_with_line_limit,
    run_check as run_hygiene_check,
)


RUFF_SELECT = ["F", "B", "SIM", "C4", "UP"]
RUFF_IGNORE = ["B017", "UP015", "UP035"]


def _env_flag(name: str, default: bool) -> bool:
    """Parse a boolean-like environment variable."""
    raw = os.getenv(name)
    if raw is None:
        return default
    return raw.strip().lower() in {"1", "true", "yes", "on"}


def _load_json_report(path: Path) -> tuple[dict[str, Any] | None, str | None]:
    """Return parsed JSON report contents or a descriptive error."""
    if not path.exists():
        return None, f"report missing: {path}"
    try:
        payload = json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError as exc:
        return None, f"report invalid JSON: {path} ({exc})"
    if not isinstance(payload, dict):
        return None, f"report payload must be an object: {path}"
    return payload, None


def _sonarqube_gate_status_from_report(payload: dict[str, Any]) -> str:
    """Extract a SonarQube quality-gate status from a report payload."""
    project_status = payload.get("projectStatus")
    if isinstance(project_status, dict):
        status = project_status.get("status")
        if isinstance(status, str):
            return status
    status = payload.get("status")
    if isinstance(status, str):
        return status
    return ""


def _fetch_sonarqube_gate_status(
    host_url: str,
    project_key: str,
    token: str,
    timeout_seconds: float,
) -> tuple[str, str | None]:
    """Query SonarQube for the project's current quality-gate status."""
    query = urllib.parse.urlencode({"projectKey": project_key})
    request = urllib.request.Request(
        f"{host_url.rstrip('/')}/api/qualitygates/project_status?{query}",
        method="GET",
    )
    if token:
        encoded = base64.b64encode(f"{token}:".encode()).decode()
        request.add_header("Authorization", f"Basic {encoded}")
    try:
        with urllib.request.urlopen(request, timeout=timeout_seconds) as response:
            payload = json.loads(response.read().decode("utf-8"))
    except (urllib.error.HTTPError, urllib.error.URLError, TimeoutError, json.JSONDecodeError) as exc:
        return "", f"sonarqube query failed: {exc}"
    if not isinstance(payload, dict):
        return "", "sonarqube query returned non-object payload"
    status = _sonarqube_gate_status_from_report(payload)
    if status:
        return status, None
    return "", "sonarqube response missing projectStatus.status"


def _run_sonarqube_check(build_dir: Path) -> dict[str, Any]:
    """Enforce SonarQube quality gate using report or API evidence."""
    enforce = _env_flag("QUALITY_GATE_SONARQUBE_ENFORCE", default=True)
    report_rel = os.getenv(
        "QUALITY_GATE_SONARQUBE_REPORT",
        str(build_dir / "sonarqube-quality-gate.json"),
    )
    report_path = Path(report_rel)
    if not report_path.is_absolute():
        report_path = Path.cwd() / report_path
    host_url = os.getenv("SONARQUBE_HOST_URL", "").strip()
    project_key = os.getenv("SONARQUBE_PROJECT_KEY", "").strip()
    token = os.getenv("SONARQUBE_TOKEN", "").strip()
    timeout_seconds = float(os.getenv("QUALITY_GATE_SONARQUBE_TIMEOUT_SECONDS", "12"))

    gate_status = ""
    source = ""
    issues: list[str] = []

    report_payload, report_error = _load_json_report(report_path)
    if report_payload is not None:
        gate_status = _sonarqube_gate_status_from_report(report_payload).strip()
        source = "report"
        if not gate_status:
            issues.append("sonarqube report missing quality gate status")
    elif report_error:
        if host_url and project_key:
            gate_status, query_error = _fetch_sonarqube_gate_status(host_url, project_key, token, timeout_seconds)
            source = "api"
            if query_error:
                issues.append(query_error)
        else:
            issues.append(report_error)

    if not source and host_url and project_key:
        gate_status, query_error = _fetch_sonarqube_gate_status(host_url, project_key, token, timeout_seconds)
        source = "api"
        if query_error:
            issues.append(query_error)

    normalized = gate_status.upper()
    passed = normalized in {"OK", "PASS", "PASSED"}
    if enforce and not passed:
        if gate_status:
            issues.append(f"sonarqube gate is {gate_status}, expected OK")
        else:
            issues.append("sonarqube gate status unavailable")

    status = "ok" if (passed or not enforce) and not issues else "failed"
    return _result(
        "sonarqube",
        "SonarQube quality gate must pass for the current project.",
        status,
        enforce=enforce,
        source=source or "none",
        gate_status=gate_status or "unknown",
        report_path=str(report_path),
        issues=issues,
    )


def _ironbank_status_from_report(payload: dict[str, Any]) -> tuple[str, bool | None]:
    """Extract a compliance status and explicit compliance flag from report payload."""
    for key in ("status", "result", "compliance", "compliance_status"):
        value = payload.get(key)
        if isinstance(value, str) and value.strip():
            return value.strip(), None
    compliant = payload.get("compliant")
    if isinstance(compliant, bool):
        return "compliant" if compliant else "noncompliant", compliant
    return "", None


def _run_ironbank_check(build_dir: Path) -> dict[str, Any]:
    """Enforce Iron Bank image-hardening compliance from build evidence."""
    enforce = _env_flag("QUALITY_GATE_IRONBANK_ENFORCE", default=True)
    required = _env_flag("QUALITY_GATE_IRONBANK_REQUIRED", default=True)
    report_rel = os.getenv(
        "QUALITY_GATE_IRONBANK_REPORT",
        str(build_dir / "ironbank-compliance.json"),
    )
    report_path = Path(report_rel)
    if not report_path.is_absolute():
        report_path = Path.cwd() / report_path

    issues: list[str] = []
    status_value = ""
    compliant: bool | None = None
    source = "none"

    report_payload, report_error = _load_json_report(report_path)
    if report_payload is not None:
        status_value, compliant = _ironbank_status_from_report(report_payload)
        source = "report"
    elif required:
        issues.append(report_error or f"report missing: {report_path}")

    normalized = status_value.strip().lower()
    passed_status = normalized in {"ok", "pass", "passed", "compliant", "true"}
    passed = compliant is True or passed_status

    if enforce and required and not passed:
        if status_value:
            issues.append(f"ironbank compliance is {status_value}, expected compliant")
        elif not issues:
            issues.append("ironbank compliance status unavailable")

    status = "ok" if (passed or not enforce or not required) and not issues else "failed"
    return _result(
        "ironbank",
        "Iron Bank image-hardening compliance must pass for build artifacts.",
        status,
        enforce=enforce,
        required=required,
        source=source,
        compliance=status_value or "unknown",
        report_path=str(report_path),
        issues=issues,
    )


def _status_from_issues(issues: list[str]) -> str:
    """Map an issue list to the gate status string."""
    return "ok" if not issues else "failed"


def _result(name: str, description: str, status: str, **extra: Any) -> dict[str, Any]:
    """Build a JSON-serializable result record for the summary payload."""
    return {"name": name, "description": description, "status": status, **extra}


def _run_ruff(contract: dict[str, Any], root: Path) -> dict[str, Any]:
    """Run the pedantic ruff subset against the contract's lint paths."""
    command = [
        sys.executable,
        "-m",
        "ruff",
        "check",
        "--select",
        ",".join(RUFF_SELECT),
        "--ignore",
        ",".join(RUFF_IGNORE),
        *contract.get("lint_paths", []),
    ]
    started_at = time.monotonic()
    completed = subprocess.run(command, cwd=root, check=False)
    return _result(
        "smell",
        "Code-smell lint for managed Python automation.",
        "ok" if completed.returncode == 0 else "failed",
        returncode=completed.returncode,
        command=command,
        duration_seconds=round(time.monotonic() - started_at, 3),
    )


def _run_pytest_suite(root: Path, suite_name: str, suite: dict[str, Any]) -> dict[str, Any]:
    """Run a pytest suite and collect junit/coverage artifact locations."""
    junit_path = root / suite["junit"]
    junit_path.parent.mkdir(parents=True, exist_ok=True)
    command = [
        sys.executable,
        "-m",
        "pytest",
        "-q",
        *suite.get("paths", []),
        f"--junitxml={junit_path}",
    ]
    coverage_xml = suite.get("coverage_xml")
    if coverage_xml:
        for source in suite.get("coverage_sources", []):
            command.append(f"--cov={source}")
        command.extend(
            [
                "--cov-branch",
                f"--cov-report=xml:{root / coverage_xml}",
            ]
        )
    started_at = time.monotonic()
    completed = subprocess.run(command, cwd=root, check=False)
    return _result(
        suite_name,
        suite["description"],
        "ok" if completed.returncode == 0 else "failed",
        returncode=completed.returncode,
        command=command,
        junit=str(junit_path.relative_to(root)),
        coverage_xml=coverage_xml,
        duration_seconds=round(time.monotonic() - started_at, 3),
    )


def run_profile(
    contract: dict[str, Any],
    root: Path,
    profile_name: str,
    build_dir: Path,
) -> dict[str, Any]:
    """Execute the configured profile and return a JSON-serializable summary."""
    build_dir.mkdir(parents=True, exist_ok=True)
    results: list[dict[str, Any]] = []
    profiles = contract.get("profiles", {})
    if profile_name not in profiles:
        raise SystemExit(f"unknown profile: {profile_name}")

    for check_name in profiles[profile_name]:
        if check_name == "docs":
            issues = run_docs_check(contract, root)
            results.append(
                _result(
                    "docs",
                    "Required docs, contract descriptions, and module docstrings.",
                    _status_from_issues(issues),
                    issues=issues,
                )
            )
            continue
        if check_name == "smell":
            results.append(_run_ruff(contract, root))
            continue
        if check_name == "hygiene":
            issues = run_hygiene_check(contract, root)
            results.append(
                _result(
                    "hygiene",
                    "500 LOC hygiene and naming rules for managed test automation.",
                    _status_from_issues(issues),
                    issues=issues,
                )
            )
            continue
        if check_name == "coverage":
            unit_suite = contract.get("pytest_suites", {}).get("unit", {})
            coverage_xml = root / unit_suite.get("coverage_xml", "build/coverage-unit.xml")
            issues = run_coverage_check(contract, root, coverage_xml)
            results.append(
                _result(
                    "coverage",
                    "Per-file 95% coverage floor for tracked quality-managed modules.",
                    _status_from_issues(issues),
                    issues=issues,
                    coverage_xml=str(coverage_xml.relative_to(root)),
                )
            )
            continue
        if check_name == "sonarqube":
            results.append(_run_sonarqube_check(build_dir))
            continue
        if check_name == "ironbank":
            results.append(_run_ironbank_check(build_dir))
            continue
        suite = contract.get("pytest_suites", {}).get(check_name)
        if suite is None:
            raise SystemExit(f"profile {profile_name} references unknown check: {check_name}")
        results.append(_run_pytest_suite(root, check_name, suite))

    status = "ok" if all(item["status"] == "ok" for item in results) else "failed"
    workspace_line_coverage_percent = 0.0
    if "coverage" in profiles[profile_name]:
        unit_suite = contract.get("pytest_suites", {}).get("unit", {})
        coverage_xml_rel = unit_suite.get("coverage_xml")
        if coverage_xml_rel:
            workspace_line_coverage_percent = compute_workspace_line_coverage(
                contract,
                root,
                root / coverage_xml_rel,
            )
    return {
        "profile": profile_name,
        "status": status,
        "results": results,
        "manual_scripts": contract.get("manual_scripts", []),
        "workspace_line_coverage_percent": workspace_line_coverage_percent,
        "source_files_total": count_files_with_line_limit(contract, root),
        "source_lines_over_500": count_files_over_line_limit(contract, root),
    }


def main(argv: list[str] | None = None) -> int:
    """CLI entrypoint for the quality gate."""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--profile", default="local")
    parser.add_argument("--build-dir", default="build")
    args = parser.parse_args(argv)

    root = Path.cwd()
    build_dir = root / args.build_dir
    build_dir.mkdir(parents=True, exist_ok=True)
    contract = load_contract()
    summary = run_profile(contract, root, args.profile, build_dir)
    summary_path = build_dir / "quality-gate-summary.json"
    summary_path.write_text(json.dumps(summary, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    return 0 if summary["status"] == "ok" else 1


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