"""
Signal smoke test — runs against the local backend (port 8001).

Usage:
    cd /Users/sttil-solutions/projects/signal
    python3 tests/smoke_test.py

Or via pytest:
    pytest tests/smoke_test.py -v

What it does:
1. Starts uvicorn on port 8001 as a subprocess
2. Waits for /health to respond
3. POSTs a sample CSV to /api/upload — verifies records are scored
4. POSTs the scored records to /api/export — verifies CSV download
5. Reports PASS or FAIL with reason
6. Kills the server
"""

import json
import os
import subprocess
import sys
import time
import urllib.request
import urllib.error
from pathlib import Path

BACKEND_PORT = 8001
BASE_URL = f"http://localhost:{BACKEND_PORT}"
SIGNAL_ROOT = Path(__file__).parent.parent
SAMPLE_CSV = SIGNAL_ROOT / "test-data" / "sample-batch-01-ok.csv"


def _wait_for_ready(timeout: int = 15) -> bool:
    deadline = time.time() + timeout
    while time.time() < deadline:
        try:
            urllib.request.urlopen(f"{BASE_URL}/health", timeout=1)
            return True
        except Exception:
            time.sleep(0.5)
    return False


def _post_file(path: Path) -> dict:
    import email.mime.multipart
    import http.client

    boundary = "SignalSmokeBoundary"
    body_parts = []
    body_parts.append(f"--{boundary}\r\n".encode())
    body_parts.append(
        f'Content-Disposition: form-data; name="file"; filename="{path.name}"\r\n'.encode()
    )
    body_parts.append(b"Content-Type: text/csv\r\n\r\n")
    body_parts.append(path.read_bytes())
    body_parts.append(f"\r\n--{boundary}--\r\n".encode())
    body = b"".join(body_parts)

    conn = http.client.HTTPConnection("localhost", BACKEND_PORT, timeout=30)
    conn.request(
        "POST",
        "/api/upload",
        body=body,
        headers={"Content-Type": f"multipart/form-data; boundary={boundary}"},
    )
    resp = conn.getresponse()
    data = resp.read()
    conn.close()
    if resp.status != 200:
        raise RuntimeError(f"/api/upload returned {resp.status}: {data[:200]}")
    return json.loads(data)


def _post_export(records: list, batch_id: str | None = None) -> bytes:
    import http.client

    body = json.dumps({"records": records, "batch_id": batch_id}).encode()
    conn = http.client.HTTPConnection("localhost", BACKEND_PORT, timeout=30)
    conn.request(
        "POST",
        "/api/export",
        body=body,
        headers={"Content-Type": "application/json"},
    )
    resp = conn.getresponse()
    data = resp.read()
    conn.close()
    if resp.status != 200:
        raise RuntimeError(f"/api/export returned {resp.status}: {data[:200]}")
    return data


def run() -> bool:
    print("Signal Smoke Test")
    print("=" * 40)

    if not SAMPLE_CSV.exists():
        print(f"FAIL — sample CSV not found: {SAMPLE_CSV}")
        return False

    env = os.environ.copy()
    env.pop("SIGNAL_API_KEY", None)

    print("Starting backend on port 8001...")
    proc = subprocess.Popen(
        [
            sys.executable, "-m", "uvicorn",
            "api.main:app",
            "--host", "127.0.0.1",
            "--port", str(BACKEND_PORT),
            "--log-level", "warning",
        ],
        cwd=str(SIGNAL_ROOT / "python-backend"),
        env=env,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
    )

    try:
        print("Waiting for backend to be ready...")
        if not _wait_for_ready():
            print("FAIL — backend did not start within 15 seconds")
            return False
        print("Backend ready.")

        # Test 1: upload
        print("Uploading sample CSV...")
        result = _post_file(SAMPLE_CSV)
        total = result.get("total", 0)
        records = result.get("records", [])
        mapping = result.get("mapping_summary", {})
        if total == 0 or not records:
            print(f"FAIL — /api/upload returned 0 records. Response: {result}")
            return False
        print(f"  Upload OK: {total} records scored")

        # Test 2: mapping summary present
        if not mapping.get("mapped"):
            print("FAIL — mapping_summary missing from response")
            return False
        print(f"  Mapping OK: {list(mapping['mapped'].keys())} mapped")

        # Test 3: rule_version present
        rv = records[0].get("rule_version", "")
        if not rv:
            print("FAIL — rule_version missing from records")
            return False
        print(f"  Rule version OK: {rv}")

        # Test 4: export
        print("Exporting work queue...")
        batch_id = result.get("batch_id")
        csv_bytes = _post_export(records, batch_id=batch_id)
        lines = csv_bytes.decode("utf-8").strip().splitlines()
        if len(lines) < 2:
            print(f"FAIL — /api/export returned fewer than 2 lines: {lines}")
            return False
        print(f"  Export OK: {len(lines)} lines (header + {len(lines)-1} records)")

        print("=" * 40)
        print("PASS")
        return True

    except Exception as exc:
        print(f"FAIL — exception: {exc}")
        return False
    finally:
        proc.terminate()
        proc.wait(timeout=5)


if __name__ == "__main__":
    success = run()
    sys.exit(0 if success else 1)


def test_signal_smoke():
    """pytest entry point."""
    assert run(), "Smoke test failed"
