import asyncio
import json
import math
import re
import sys
import hashlib
from typing import List, Dict, Any, Tuple

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")

class PureVectorEmbedder:
    def __init__(self):
        self.dims = 768

    def embed(self, text: str) -> List[float]:
        vec = [0.0] * self.dims
        words = re.findall(r"\w+", text.lower(), re.UNICODE)
        if not words:
            return vec

        for w in words:
            h = int(hashlib.sha256(w.encode("utf-8")).hexdigest(), 16) % self.dims
            vec[h] += 1.0
            for i in range(len(w) - 2):
                ngram = w[i:i+3]
                nh = int(hashlib.md5(ngram.encode("utf-8")).hexdigest(), 16) % self.dims
                vec[nh] += 0.5

        norm = math.sqrt(sum(x * x for x in vec))
        if norm > 0:
            vec = [x / norm for x in vec]
        return vec

def qdrant_score(v1: List[float], v2: List[float]) -> float:
    raw_cos = sum(a * b for a, b in zip(v1, v2))
    # Qdrant converts cosine [-1, 1] to [0, 1]
    return (raw_cos + 1.0) / 2.0

from inspect_mem0 import inspect_live_mem0

async def run_simulation():
    dscons_mems, non_dscons_mems = await inspect_live_mem0()
    embedder = PureVectorEmbedder()
    
    embedded_non_dscons = []
    for m in non_dscons_mems:
        v = embedder.embed(m.get("memory", ""))
        embedded_non_dscons.append((m, v))
        
    sys.path.append("c:/Projects/DSCons/.agents/teamwork/teamwork_preview_worker_m2")
    from ingest_27_facts import FACTS_CATALOG as BASELINE_FACTS
    
    def test_facts(catalog, name="Catalog"):
        print(f"\n=================== TESTING {name} ===================")
        embedded_facts = []
        for f in catalog:
            v = embedder.embed(f["text"])
            embedded_facts.append((f, v))
            
        queries = [
            ("Q1", "DSCons rule", ["Bảy Điều luật", "cốt lõi", "synthetic data", "Định Sơn", "0202111150", "lục giác", "Hexagonal"]),
            ("Q2", "frontend theme", ["Dark Slate", "#0b0f19", "#080d1a", "Inter", "JetBrains Mono", "Bloomberg", "erp_enterprise.css"]),
            ("Q3", "backend architecture", ["Clean Architecture", "DDD", "NUMERIC(18, 4)", "Decimal", "Ports", "4-Layer", "4 tầng"]),
            ("Q4", "AEC pricing norms", ["Thông tư 38", "cừ Larsen", "VL = 0", "MR / T", "MR/T", "G_XD", "Định mức"]),
            ("Q5", "CAD takeoff geometry", ["TCVN3", "stroke-width: 1px", "Bounding Box", "Chương SA", ".VnTime", "Zero-Void"]),
            ("Q6", "database precision", ["NUMERIC(18, 4)", "Decimal", "Sổ kép", "Double-entry", "18, 4"]),
            ("Q7", "testing workflow", ["Deep Matrix", "D1-D6", "D1 Đến D6", "TDD", "task.md", "Verification", "80%"]),
            ("Q8", "MCP tools protocol", ["lsp-mcp", "chrome-devtools-mcp", "markitdown", "memory", "StitchMCP"]),
            ("Challenger-1", "bảng màu frontend", []),
            ("Challenger-2", "frontend color palette", []),
            ("Challenger-3", "độ chính xác số thực database", []),
            ("Challenger-4", "kien truc backend luc giac", []),
        ]
        
        for q_id, q_text, expected_kws in queries:
            qv = embedder.embed(q_text)
            
            scored = []
            for f, fv in embedded_facts:
                sim = qdrant_score(qv, fv)
                scored.append((sim, True, f["code"], f["text"]))
            for m, mv in embedded_non_dscons:
                sim = qdrant_score(qv, mv)
                text = m.get("memory", "")
                scored.append((sim, False, m.get("id"), text))
                
            scored.sort(key=lambda x: x[0], reverse=True)
            
            top1 = scored[0]
            top3 = scored[:3]
            dscons_in_top3 = sum(1 for x in top3 if x[1])
            is_top1_dscons = top1[1]
            
            kw_match = False
            matched = ""
            for kw in expected_kws:
                if kw.lower() in top1[3].lower():
                    kw_match = True
                    matched = kw
                    break
                    
            status = "PASS" if (is_top1_dscons and top1[0] >= 0.55 and dscons_in_top3 >= 2 and (kw_match or not expected_kws)) else "FAIL"
            print(f"[{status}] {q_id:<12} | Top1: {top1[0]:.4f} ({'DSCons' if is_top1_dscons else 'NON-DSC'} - {top1[2]}) | Top3 DSC: {dscons_in_top3}/3 | KwMatch: {kw_match} ({matched})")
            if status == "FAIL" or not is_top1_dscons:
                for rank, hit in enumerate(top3, 1):
                    code_or_id = hit[2] if hit[1] else f"FOREIGN({hit[2][:8]})"
                    print(f"       #{rank}: {hit[0]:.4f} | {'DSCons' if hit[1] else 'FOREIGN'} | {code_or_id} | {hit[3][:70]}")

    test_facts(BASELINE_FACTS, "BASELINE")

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