"""DSCons ERP Mem0 Knowledge Base Verification Test Suite (Remediated Prototype).

End-to-end automated verification harness for the Mem0 long-term memory engine
backing DSCons ERP (Công ty TNHH Xây Dựng Định Sơn - MST: 0202111150).

Remediations applied:
1. Support project="DSCons" in Mem0Client.search() to isolate DSCons project context.
2. Remove false-positive forbidden keywords ("FLOAT cho tiền tệ", "DOUBLE cho tiền tệ", "scaffolding rỗng").
3. Make keyword relevance check evaluate across Top-3 returned DSCons hits (Recall@3).
"""

import asyncio
import json
import sys
from typing import Any

import pytest
from mcp.client.session import ClientSession
from mcp.client.sse import sse_client

# Ensure safe UTF-8 encoding on Windows consoles
if hasattr(sys.stdout, "reconfigure"):
    sys.stdout.reconfigure(encoding="utf-8", errors="replace")
if hasattr(sys.stderr, "reconfigure"):
    sys.stderr.reconfigure(encoding="utf-8", errors="replace")


# Service configuration
MEM0_SSE_URL = "http://127.0.0.1:8765/sse"
USER_ID = "developer"
DEFAULT_PROJECT = "DSCons"
MIN_SIMILARITY_SCORE = 0.55

# 8 Verification Queries Catalog
VERIFICATION_CATALOG: list[dict[str, Any]] = [
    {
        "id": "Q1",
        "name": "Mandatory Q1 - DSCons Root Rules & Core Laws",
        "query": "DSCons rule",
        "mandatory": True,
        "area": "Area 1: Root Standards",
        "expected_keywords": [
            "Bảy Điều luật",
            "cốt lõi",
            "synthetic data",
            "Định Sơn",
            "0202111150",
            "lục giác",
            "Hexagonal",
        ],
        "forbidden_keywords": [
            "app/core/logging_config",
            "FreeExile",
            "Atlas",
            "Savage",
            "Feral NPC",
        ],
    },
    {
        "id": "Q2",
        "name": "Mandatory Q2 - Frontend UI/UX Theme",
        "query": "frontend theme",
        "mandatory": True,
        "area": "Area 3: Frontend UI/UX",
        "expected_keywords": [
            "Dark Slate",
            "#0b0f19",
            "#080d1a",
            "Inter",
            "JetBrains Mono",
            "Bloomberg",
            "erp_enterprise.css",
        ],
        "forbidden_keywords": [
            "Feral NPC",
            "Dialogue HUD",
            "Combat UI",
            "FreeExile",
            "Atlas",
        ],
    },
    {
        "id": "Q3",
        "name": "Mandatory Q3 - Backend Clean Architecture & DDD",
        "query": "backend architecture",
        "mandatory": True,
        "area": "Area 2: Backend & DB",
        "expected_keywords": [
            "Clean Architecture",
            "DDD",
            "NUMERIC(18, 4)",
            "Decimal",
            "Ports",
            "4-Layer",
            "4 tầng",
        ],
        "forbidden_keywords": [
            "Server-Authoritative Streaming",
            "Cocos Creator",
            "FreeExile",
            "Feral",
        ],
    },
    {
        "id": "Q4",
        "name": "Domain Q4 - AEC Pricing Norms & BoQ Budgeting",
        "query": "AEC pricing norms",
        "mandatory": False,
        "area": "Area 4: AEC Business",
        "expected_keywords": [
            "Thông tư 38",
            "cừ Larsen",
            "VL = 0",
            "MR / T",
            "MR/T",
            "G_XD",
            "Định mức",
        ],
        "forbidden_keywords": [
            "4.753 tỷ",
            "cache 71",
            "FreeExile",
        ],
    },
    {
        "id": "Q5",
        "name": "Domain Q5 - CAD Takeoff Engine Geometry",
        "query": "CAD takeoff geometry",
        "mandatory": False,
        "area": "Area 4: CAD Takeoff",
        "expected_keywords": [
            "TCVN3",
            "stroke-width: 1px",
            "Bounding Box",
            "Chương SA",
            ".VnTime",
            "Zero-Void",
        ],
        "forbidden_keywords": [
            "FreeExile",
            "Combat",
            "NPC",
        ],
    },
    {
        "id": "Q6",
        "name": "Domain Q6 - Database Precision & Financial Ledgers",
        "query": "database precision",
        "mandatory": False,
        "area": "Area 2: Backend & DB",
        "expected_keywords": [
            "NUMERIC(18, 4)",
            "Decimal",
            "Sổ kép",
            "Double-entry",
            "18, 4",
        ],
        "forbidden_keywords": [
            "FreeExile",
        ],
    },
    {
        "id": "Q7",
        "name": "Domain Q7 - Testing Workflow & Quality Gates",
        "query": "testing workflow",
        "mandatory": False,
        "area": "Area 5: Testing & Workflow",
        "expected_keywords": [
            "Deep Matrix",
            "D1-D6",
            "D1 Đến D6",
            "TDD",
            "task.md",
            "Verification",
            "80%",
        ],
        "forbidden_keywords": [
            "FreeExile",
        ],
    },
    {
        "id": "Q8",
        "name": "Domain Q8 - Mandatory MCP Tools Protocol",
        "query": "MCP tools protocol",
        "mandatory": False,
        "area": "Area 1: Root Standards",
        "expected_keywords": [
            "lsp-mcp",
            "chrome-devtools-mcp",
            "markitdown",
            "memory",
            "StitchMCP",
        ],
        "forbidden_keywords": [
            "grep_search cho đổi tên symbol",
            "FreeExile",
        ],
    },
]


class Mem0Client:
    """Lightweight async MCP client for querying the local Mem0 FastMCP background service."""

    def __init__(self, sse_url: str = MEM0_SSE_URL):
        self.sse_url = sse_url

    async def check_status(self) -> dict[str, Any]:
        """Check Mem0 service operational status."""
        async with (
            sse_client(self.sse_url) as (read, write),
            ClientSession(read, write) as session,
        ):
            await session.initialize()
            res = await session.call_tool("mem0_status", arguments={})
            return json.loads(res.content[0].text)

    async def search(
        self, query: str, limit: int = 5, project: str | None = DEFAULT_PROJECT
    ) -> list[dict[str, Any]]:
        """Search Mem0 long-term memory for matching facts."""
        async with (
            sse_client(self.sse_url) as (read, write),
            ClientSession(read, write) as session,
        ):
            await session.initialize()
            arguments: dict[str, Any] = {"query": query, "user_id": USER_ID, "limit": limit}
            if project:
                arguments["project"] = project
            res = await session.call_tool("mem0_search", arguments=arguments)
            data = json.loads(res.content[0].text)
            return data.get("memories", {}).get("results", [])

    async def get_all(self) -> list[dict[str, Any]]:
        """Fetch all stored memories for developer."""
        async with (
            sse_client(self.sse_url) as (read, write),
            ClientSession(read, write) as session,
        ):
            await session.initialize()
            res = await session.call_tool(
                "mem0_get_all", arguments={"user_id": USER_ID}
            )
            data = json.loads(res.content[0].text)
            return data.get("memories", {}).get("results", [])


def is_dscons_record(item: dict[str, Any]) -> bool:
    """Determine whether a returned record belongs to the DSCons ERP domain."""
    text = item.get("memory", "")
    metadata = item.get("metadata") or {}
    proj = str(metadata.get("project", "")).strip().lower()

    if proj == "dscons":
        return True
    return bool(text.startswith("[DSCons") or "DSCons ERP" in text or "Định Sơn" in text)


def evaluate_query_result(
    catalog_entry: dict[str, Any], results: list[dict[str, Any]]
) -> dict[str, Any]:
    """Evaluate a single query's results against pass/fail criteria."""
    entry_id = catalog_entry["id"]
    query = catalog_entry["query"]
    expected_kws = catalog_entry["expected_keywords"]
    forbidden_kws = catalog_entry["forbidden_keywords"]

    evaluation = {
        "id": entry_id,
        "name": catalog_entry["name"],
        "query": query,
        "mandatory": catalog_entry["mandatory"],
        "total_hits": len(results),
        "top1_score": 0.0,
        "score_pass": False,
        "domain_dominance_pass": False,
        "contamination_clean": True,
        "expected_keyword_found": False,
        "matched_keyword": None,
        "contamination_reasons": [],
        "overall_pass": False,
        "top1_snippet": "",
        "top3_items": [],
    }

    if not results:
        evaluation["contamination_reasons"].append("No results returned from query")
        return evaluation

    top1 = results[0]
    top1_text = top1.get("memory", "")
    top1_score = float(top1.get("score", 0.0))
    evaluation["top1_score"] = top1_score
    evaluation["top1_snippet"] = (
        (top1_text[:90] + "...") if len(top1_text) > 90 else top1_text
    )

    # 1. Gate 1: Top-1 Score >= 0.55
    evaluation["score_pass"] = top1_score >= MIN_SIMILARITY_SCORE

    # 2. Gate 2: Domain Dominance
    # Top-1 MUST be DSCons
    top1_is_dscons = is_dscons_record(top1)
    # Check Top-3
    top3 = results[:3]
    top3_dscons_count = sum(1 for r in top3 if is_dscons_record(r))
    top3_ratio = top3_dscons_count / len(top3) if top3 else 0.0

    evaluation["domain_dominance_pass"] = (
        top1_is_dscons and (top3_ratio >= 0.66) and bool(top1_is_dscons)
    )

    # 3. Gate 3: Keyword Relevance across Top-3 hits (Recall@3)
    for r in top3:
        r_text = r.get("memory", "")
        for kw in expected_kws:
            if kw.lower() in r_text.lower():
                evaluation["expected_keyword_found"] = True
                evaluation["matched_keyword"] = kw
                break
        if evaluation["expected_keyword_found"]:
            break

    # Contamination check across Top-3
    for i, r in enumerate(top3):
        r_text = r.get("memory", "")
        r_meta = r.get("metadata") or {}
        r_proj = str(r_meta.get("project", "")).lower()

        # Check forbidden keywords
        for fkw in forbidden_kws:
            if fkw.lower() in r_text.lower():
                evaluation["contamination_clean"] = False
                evaluation["contamination_reasons"].append(
                    f"Hit #{i+1} contains forbidden keyword '{fkw}'"
                )

        # Cross-project contamination check
        if (
            "freeexile" in r_text.lower() or "feral npc" in r_text.lower()
        ) and r_proj == "dscons":
            evaluation["contamination_clean"] = False
            evaluation["contamination_reasons"].append(
                f"Hit #{i+1} has corrupted metadata (FreeExile tagged as DSCons)"
            )

        evaluation["top3_items"].append(
            {
                "rank": i + 1,
                "score": r.get("score", 0.0),
                "is_dscons": is_dscons_record(r),
                "snippet": (r_text[:70] + "...") if len(r_text) > 70 else r_text,
            }
        )

    # Overall Verdict
    evaluation["overall_pass"] = (
        evaluation["score_pass"]
        and evaluation["domain_dominance_pass"]
        and evaluation["contamination_clean"]
        and evaluation["expected_keyword_found"]
    )

    return evaluation


def run_all_verifications() -> tuple[bool, list[dict[str, Any]]]:
    """Execute all 8 verification queries synchronously via asyncio and return scorecard."""
    client = Mem0Client()

    async def _async_run():
        status = await client.check_status()
        evaluations = []
        for entry in VERIFICATION_CATALOG:
            results = await client.search(entry["query"], limit=5)
            ev = evaluate_query_result(entry, results)
            evaluations.append(ev)
        return status, evaluations

    _status, evaluations = asyncio.run(_async_run())
    all_passed = all(ev["overall_pass"] for ev in evaluations)
    return all_passed, evaluations


def print_scorecard(evaluations: list[dict[str, Any]], all_passed: bool) -> None:
    """Print an attractive, aligned ASCII scorecard table to stdout."""
    border = "=" * 94
    sub_border = "-" * 94

    print("\n" + border)
    print("                     DSCONS ERP MEM0 LONG-TERM MEMORY SCORECARD")
    print(border)
    print(
        f" {'ID':<3} | {'Type':<5} | {'Query String':<24} | {'Top-1':<7} | {'Domain':<8} | {'Contam':<8} | {'Status':<6}"
    )
    print(sub_border)

    for ev in evaluations:
        q_id = ev["id"]
        q_type = "Man" if ev["mandatory"] else "Dom"
        query_str = (
            ev["query"][:22] + ".." if len(ev["query"]) > 24 else ev["query"]
        )
        score_str = f"{ev['top1_score']:.4f}"
        domain_str = "DSCons" if ev["domain_dominance_pass"] else "FAIL"
        contam_str = "CLEAN" if ev["contamination_clean"] else "DIRTY"
        status_str = "PASS" if ev["overall_pass"] else "FAIL"

        print(
            f" {q_id:<3} | {q_type:<5} | {query_str:<24} | {score_str:<7} | {domain_str:<8} | {contam_str:<8} | {status_str:<6}"
        )

    print(border)

    passed_count = sum(1 for ev in evaluations if ev["overall_pass"])
    total_count = len(evaluations)
    pass_pct = (passed_count / total_count) * 100.0

    print(
        f" SUMMARY: {passed_count}/{total_count} Queries Passed ({pass_pct:.1f}%)"
    )

    if all_passed:
        print(" FINAL VERDICT: [PASS] All 8 queries met 100% acceptance criteria.")
    else:
        print(" FINAL VERDICT: [FAIL] System requires cleanup and/or ingestion.")
        print("\n--- FAILURE DIAGNOSTICS ---")
        for ev in evaluations:
            if not ev["overall_pass"]:
                print(f"\n[!] Failure in {ev['id']} - \"{ev['query']}\":")
                print(
                    f"    - Score: {ev['top1_score']:.4f} (>= {MIN_SIMILARITY_SCORE}: {ev['score_pass']})"
                )
                print(f"    - Domain Dominance: {ev['domain_dominance_pass']}")
                print(
                    f"    - Keyword Found: {ev['expected_keyword_found']} (Matched: {ev['matched_keyword']})"
                )
                print(f"    - Contamination Clean: {ev['contamination_clean']}")
                if ev["contamination_reasons"]:
                    for cr in ev["contamination_reasons"]:
                        print(f"      * Contamination: {cr}")
                print(f"    - Top 1 Snippet: {ev['top1_snippet']}")
                if ev["top3_items"]:
                    print("    - Top Hits:")
                    for it in ev["top3_items"]:
                        print(
                            f"      #{it['rank']}: score={it['score']:.4f} is_dscons={it['is_dscons']} text={it['snippet']}"
                        )
    print(border + "\n")


# =============================================================================
# Pytest Test Functions (Automated Test Execution)
# =============================================================================


@pytest.fixture(scope="module")
def verification_results():
    """Module fixture caching the 8 verification query evaluations."""
    _all_passed, evaluations = run_all_verifications()
    return {ev["id"]: ev for ev in evaluations}


def test_q1_dscons_root_rules(verification_results):
    """Verify Mandatory Q1: DSCons root rules and core laws."""
    ev = verification_results["Q1"]
    assert (
        ev["score_pass"]
    ), f"Top-1 score {ev['top1_score']:.4f} < {MIN_SIMILARITY_SCORE}"
    assert (
        ev["domain_dominance_pass"]
    ), f"Domain dominance failed for {ev['query']}"
    assert (
        ev["contamination_clean"]
    ), f"Contamination detected: {ev['contamination_reasons']}"
    assert (
        ev["expected_keyword_found"]
    ), "No expected DSCons root rule keywords found in Top-3"
    assert ev["overall_pass"]


def test_q2_frontend_theme(verification_results):
    """Verify Mandatory Q2: Frontend UI/UX Dark Slate theme."""
    ev = verification_results["Q2"]
    assert (
        ev["score_pass"]
    ), f"Top-1 score {ev['top1_score']:.4f} < {MIN_SIMILARITY_SCORE}"
    assert (
        ev["domain_dominance_pass"]
    ), f"Domain dominance failed for {ev['query']}"
    assert (
        ev["contamination_clean"]
    ), f"Contamination detected: {ev['contamination_reasons']}"
    assert (
        ev["expected_keyword_found"]
    ), "No Dark Slate / UI theme keywords found in Top-3"
    assert ev["overall_pass"]


def test_q3_backend_architecture(verification_results):
    """Verify Mandatory Q3: Backend Clean Architecture & DDD."""
    ev = verification_results["Q3"]
    assert (
        ev["score_pass"]
    ), f"Top-1 score {ev['top1_score']:.4f} < {MIN_SIMILARITY_SCORE}"
    assert (
        ev["domain_dominance_pass"]
    ), f"Domain dominance failed for {ev['query']}"
    assert (
        ev["contamination_clean"]
    ), f"Contamination detected: {ev['contamination_reasons']}"
    assert (
        ev["expected_keyword_found"]
    ), "No Clean Architecture keywords found in Top-3"
    assert ev["overall_pass"]


def test_q4_aec_pricing_norms(verification_results):
    """Verify Domain Q4: AEC pricing norms & Circular 38."""
    ev = verification_results["Q4"]
    assert (
        ev["score_pass"]
    ), f"Top-1 score {ev['top1_score']:.4f} < {MIN_SIMILARITY_SCORE}"
    assert (
        ev["domain_dominance_pass"]
    ), f"Domain dominance failed for {ev['query']}"
    assert (
        ev["contamination_clean"]
    ), f"Contamination detected: {ev['contamination_reasons']}"
    assert (
        ev["expected_keyword_found"]
    ), "No Circular 38 / pricing norms keywords found in Top-3"
    assert ev["overall_pass"]


def test_q5_cad_takeoff_geometry(verification_results):
    """Verify Domain Q5: CAD takeoff engine & TCVN3 decoding."""
    ev = verification_results["Q5"]
    assert (
        ev["score_pass"]
    ), f"Top-1 score {ev['top1_score']:.4f} < {MIN_SIMILARITY_SCORE}"
    assert (
        ev["domain_dominance_pass"]
    ), f"Domain dominance failed for {ev['query']}"
    assert (
        ev["contamination_clean"]
    ), f"Contamination detected: {ev['contamination_reasons']}"
    assert (
        ev["expected_keyword_found"]
    ), "No CAD takeoff / TCVN3 keywords found in Top-3"
    assert ev["overall_pass"]


def test_q6_database_precision(verification_results):
    """Verify Domain Q6: Database precision NUMERIC(18,4) & ledgers."""
    ev = verification_results["Q6"]
    assert (
        ev["score_pass"]
    ), f"Top-1 score {ev['top1_score']:.4f} < {MIN_SIMILARITY_SCORE}"
    assert (
        ev["domain_dominance_pass"]
    ), f"Domain dominance failed for {ev['query']}"
    assert (
        ev["contamination_clean"]
    ), f"Contamination detected: {ev['contamination_reasons']}"
    assert (
        ev["expected_keyword_found"]
    ), "No NUMERIC(18,4) / precision keywords found in Top-3"
    assert ev["overall_pass"]


def test_q7_testing_workflow(verification_results):
    """Verify Domain Q7: Deep Matrix D1-D6 testing workflow & TDD."""
    ev = verification_results["Q7"]
    assert (
        ev["score_pass"]
    ), f"Top-1 score {ev['top1_score']:.4f} < {MIN_SIMILARITY_SCORE}"
    assert (
        ev["domain_dominance_pass"]
    ), f"Domain dominance failed for {ev['query']}"
    assert (
        ev["contamination_clean"]
    ), f"Contamination detected: {ev['contamination_reasons']}"
    assert (
        ev["expected_keyword_found"]
    ), "No Deep Matrix / TDD workflow keywords found in Top-3"
    assert ev["overall_pass"]


def test_q8_mcp_tools_protocol(verification_results):
    """Verify Domain Q8: Mandatory MCP tools protocol."""
    ev = verification_results["Q8"]
    assert (
        ev["score_pass"]
    ), f"Top-1 score {ev['top1_score']:.4f} < {MIN_SIMILARITY_SCORE}"
    assert (
        ev["domain_dominance_pass"]
    ), f"Domain dominance failed for {ev['query']}"
    assert (
        ev["contamination_clean"]
    ), f"Contamination detected: {ev['contamination_reasons']}"
    assert (
        ev["expected_keyword_found"]
    ), "No MCP tools protocol keywords found in Top-3"
    assert ev["overall_pass"]


# =============================================================================
# CLI Main Entry Point
# =============================================================================

if __name__ == "__main__":
    print("Initiating DSCons ERP Mem0 Knowledge Base Verification Run (Remediated Prototype)...")
    all_passed, evaluations = run_all_verifications()
    print_scorecard(evaluations, all_passed)
    sys.exit(0 if all_passed else 1)
