"""
Integration test for EPIC 2: State Management & Resources.

Tests the full lifecycle:
  store_scan → get_scan → get_scan_results (filtered) → generate_report → resources
"""

import asyncio
import json
import shutil
import tempfile
from pathlib import Path

# ── Patch dirs to temp so tests don't pollute the user's home ──
_tmp = Path(tempfile.mkdtemp(prefix="s2l_test_"))
_db_path = _tmp / "scans.db"
_results_dir = _tmp / "results"
_reports_dir = _tmp / "reports"


def _hr(title: str) -> None:
    print(f"\n{'='*60}\n  {title}\n{'='*60}")


async def main() -> None:
    # ── 1. State Management ──────────────────────────────────────
    _hr("EPIC 2.1 — ScanStateManager")

    from supreme_max.mcp_server.state import ScanStateManager

    mgr = ScanStateManager(db_path=_db_path, results_dir=_results_dir)

    fake_scan = {
        "project_path": "/tmp/my-project",
        "timestamp": "2026-02-10T12:00:00Z",
        "scanners_used": ["PythonScanner", "JavaScriptScanner"],
        "files_scanned": 42,
        "issues_found": 5,
        "scan_time": 3.14,
        "issues": [
            {"severity": "CRITICAL", "message": "SQL injection", "file": "app/db.py",
             "scanner": "PythonScanner", "rule_id": "B608", "line": 10},
            {"severity": "HIGH", "message": "XSS vulnerability", "file": "app/views.js",
             "scanner": "JavaScriptScanner", "rule_id": "XSS01", "line": 22},
            {"severity": "MEDIUM", "message": "Weak hash", "file": "app/auth.py",
             "scanner": "PythonScanner", "rule_id": "B303", "line": 5},
            {"severity": "LOW", "message": "Print statement", "file": "app/utils.py",
             "scanner": "PythonScanner", "rule_id": "T201", "line": 15},
            {"severity": "MEDIUM", "message": "Missing CSRF", "file": "app/views.js",
             "scanner": "JavaScriptScanner", "rule_id": "SEC02", "line": 30},
        ],
    }

    scan_id = mgr.store_scan(fake_scan)
    print(f"  ✅ store_scan → scan_id = {scan_id}")

    # get_scan by id
    result = mgr.get_scan(scan_id)
    assert result is not None, "get_scan returned None"
    assert result["project_path"] == "/tmp/my-project"
    print(f"  ✅ get_scan(id) → project_path = {result['project_path']}")

    # get_scan("latest")
    latest = mgr.get_scan("latest")
    assert latest is not None
    print(f"  ✅ get_scan('latest') → OK")

    # get_latest_scan
    latest2 = mgr.get_latest_scan()
    assert latest2 is not None
    print(f"  ✅ get_latest_scan() → OK")

    # get_scan_metadata
    meta = mgr.get_scan_metadata(scan_id)
    assert meta is not None
    assert meta["files_scanned"] == 42
    assert meta["scanners_used"] == ["PythonScanner", "JavaScriptScanner"]
    print(f"  ✅ get_scan_metadata() → scanners = {meta['scanners_used']}")

    # update_scan_report_path
    mgr.update_scan_report_path(scan_id, "/tmp/report.json")
    meta2 = mgr.get_scan_metadata(scan_id)
    assert meta2["report_path"] == "/tmp/report.json"
    print(f"  ✅ update_scan_report_path() → report_path = {meta2['report_path']}")

    # list_scans
    scans = mgr.list_scans()
    assert len(scans) == 1
    print(f"  ✅ list_scans() → count = {len(scans)}")

    # ── 2. get_scan_results ──────────────────────────────────────
    _hr("EPIC 2.2 — get_scan_results")

    from supreme_max.mcp_server import tools as tools_mod

    # Patch the tools module to use our test state manager
    tools_mod._state_manager = mgr

    from supreme_max.mcp_server.tools import get_scan_results

    # No filters
    r = await get_scan_results(scan_id)
    assert len(r["issues"]) == 5
    print(f"  ✅ No filters → {len(r['issues'])} issues")

    # Filter by severity
    r = await get_scan_results(scan_id, filters={"severity": ["CRITICAL", "HIGH"]})
    assert len(r["issues"]) == 2
    print(f"  ✅ severity=[CRITICAL,HIGH] → {len(r['issues'])} issues")

    # Filter by severity (case insensitive)
    r = await get_scan_results(scan_id, filters={"severity": ["critical"]})
    assert len(r["issues"]) == 1
    print(f"  ✅ severity=[critical] (lower) → {len(r['issues'])} issues")

    # Filter by file_pattern
    r = await get_scan_results(scan_id, filters={"file_pattern": r"\.js$"})
    assert len(r["issues"]) == 2
    print(f"  ✅ file_pattern=\\.js$ → {len(r['issues'])} issues")

    # Filter by scanner
    r = await get_scan_results(scan_id, filters={"scanner": "PythonScanner"})
    assert len(r["issues"]) == 3
    print(f"  ✅ scanner=PythonScanner → {len(r['issues'])} issues")

    # Filter by rule_id
    r = await get_scan_results(scan_id, filters={"rule_id": "B608"})
    assert len(r["issues"]) == 1
    print(f"  ✅ rule_id=B608 → {len(r['issues'])} issues")

    # Combined filters
    r = await get_scan_results(scan_id, filters={"severity": ["MEDIUM"], "scanner": "JavaScriptScanner"})
    assert len(r["issues"]) == 1
    print(f"  ✅ severity=MEDIUM + scanner=JS → {len(r['issues'])} issues")

    # Limit
    r = await get_scan_results(scan_id, limit=2)
    assert len(r["issues"]) == 2
    print(f"  ✅ limit=2 → {len(r['issues'])} issues")

    # "latest" alias
    r = await get_scan_results("latest")
    assert r["scan_id"] == scan_id
    print(f"  ✅ scan_id='latest' resolved → {r['scan_id']}")

    # Not found
    r = await get_scan_results("nonexistent")
    assert "error" in r
    print(f"  ✅ Not found → {r['error']}")

    # Verify original data not modified
    original = mgr.get_scan(scan_id)
    assert len(original["issues"]) == 5, "Original data was mutated!"
    print(f"  ✅ Original data intact → {len(original['issues'])} issues")

    # ── 3. generate_report ───────────────────────────────────────
    _hr("EPIC 2.3 — generate_report")

    from supreme_max.mcp_server.tools import generate_report
    import supreme_max.mcp_server.tools as t

    # Patch reports dir
    t._REPORTS_DIR = _reports_dir

    # Reset report_path for a clean test
    mgr.update_scan_report_path(scan_id, "")

    rr = await generate_report(scan_id)
    assert "error" not in rr, f"generate_report error: {rr}"
    assert rr["format"] == "json"
    assert rr["message"] == "Report generated successfully"
    print(f"  ✅ generate_report() → {rr['report_path']}")

    # Verify the report file exists and has correct structure
    report_path = Path(rr["report_path"])
    assert report_path.is_file()
    report = json.loads(report_path.read_text(encoding="utf-8"))

    assert "metadata" in report
    assert "summary" in report
    assert "aggregations" in report
    assert "security_score" in report
    assert "risk_level" in report
    assert "issues" in report
    print(f"  ✅ Report structure validated — keys: {list(report.keys())}")

    # Check security score calculation:
    #   CRITICAL=-30, HIGH=-15, MEDIUM=-5×2, LOW=-1 → 100-30-15-10-1 = 44
    assert report["security_score"] == 44, f"Expected 44, got {report['security_score']}"
    assert report["risk_level"] == "CONCERNING", f"Expected CONCERNING, got {report['risk_level']}"
    print(f"  ✅ Security score = {report['security_score']}, risk = {report['risk_level']}")

    # Check aggregations
    agg = report["aggregations"]
    assert agg["by_severity"]["CRITICAL"] == 1
    assert agg["by_severity"]["HIGH"] == 1
    assert agg["by_severity"]["MEDIUM"] == 2
    assert agg["by_file"]["app/db.py"] == 1
    assert agg["by_scanner"]["PythonScanner"] == 3
    print(f"  ✅ Aggregations correct")

    # Check report_path stored in DB
    meta3 = mgr.get_scan_metadata(scan_id)
    assert meta3["report_path"] == str(report_path)
    print(f"  ✅ report_path stored in DB")

    # "latest" alias
    rr2 = await generate_report("latest")
    assert "error" not in rr2
    print(f"  ✅ generate_report('latest') → OK")

    # Not found
    rr3 = await generate_report("nonexistent")
    assert "error" in rr3
    print(f"  ✅ Not found → {rr3['error']}")

    # ── 4. MCP Resources ─────────────────────────────────────────
    _hr("EPIC 2.3 — MCP Resources")

    from supreme_max.mcp_server import resources as res_mod

    # Patch state manager in resources module too
    res_mod._state_manager = mgr

    from supreme_max.mcp_server.resources import resolve_resource

    # sm://scans/latest
    content, mime = await resolve_resource("sm://scans/latest")
    assert mime == "application/json"
    data = json.loads(content)
    assert data["project_path"] == "/tmp/my-project"
    print(f"  ✅ sm://scans/latest → {mime}, project={data['project_path']}")

    # sm://scans/{scan_id}
    content, mime = await resolve_resource(f"sm://scans/{scan_id}")
    assert mime == "application/json"
    data = json.loads(content)
    assert len(data["issues"]) == 5
    print(f"  ✅ sm://scans/{scan_id} → {len(data['issues'])} issues")

    # sm://scans/{scan_id}/report
    content, mime = await resolve_resource(f"sm://scans/{scan_id}/report")
    assert mime == "application/json"
    report2 = json.loads(content)
    assert "security_score" in report2
    print(f"  ✅ sm://scans/{scan_id}/report → score={report2['security_score']}")

    # sm://config
    content, mime = await resolve_resource("sm://config")
    assert mime == "application/yaml"
    assert "scanners" in content or "version" in content
    print(f"  ✅ sm://config → {mime}, {len(content)} chars")

    # sm://scanners
    content, mime = await resolve_resource("sm://scanners")
    assert mime == "application/json"
    scanners_list = json.loads(content)
    assert isinstance(scanners_list, list)
    assert len(scanners_list) > 0
    print(f"  ✅ sm://scanners → {len(scanners_list)} scanners")

    # Invalid URI
    try:
        await resolve_resource("invalid://foo")
        assert False, "Should have raised ValueError"
    except ValueError:
        print(f"  ✅ Invalid URI → ValueError raised")

    # Unknown path
    try:
        await resolve_resource("sm://unknown/path")
        assert False, "Should have raised ValueError"
    except ValueError:
        print(f"  ✅ Unknown path → ValueError raised")

    # ── Done ─────────────────────────────────────────────────────
    _hr("ALL TESTS PASSED ✅")

    # Cleanup
    shutil.rmtree(_tmp, ignore_errors=True)


if __name__ == "__main__":
    asyncio.run(main())
