#!/usr/bin/env python3
"""
E2E Test Suite Runner for ANL Homepage Development
Verifies:
0. Clean step (npm run clean -> removes .next and tsconfig.tsbuildinfo)
1. ESLint (npm run lint)
2. Next.js Static SSG Build (npm run build) with 0 dynamic routes
3. TypeScript type check (npx tsc --noEmit)
4. Existence and content of static SSG routes HTML/body files
5. Data count losslessness & integrity gate (verify_counts.py)
6. Light/Dark mode theme tokens and CSS attributes in source and build assets
7. DOM & HTML Content Assertions (Unittest)
"""

import os
import sys
import subprocess
import re

ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
REFER_DIR = os.path.join(ROOT_DIR, "refer_landing_page")

# Expected Static SSG Routes HTML/Body output paths relative to REFER_DIR
EXPECTED_SSG_ROUTES = [
    os.path.join(".next", "server", "app", "index.html"),
    os.path.join(".next", "server", "app", "members.html"),
    os.path.join(".next", "server", "app", "publications.html"),
    os.path.join(".next", "server", "app", "publications", "intl-journal-conf.html"),
    os.path.join(".next", "server", "app", "publications", "domestic-journal-conf.html"),
    os.path.join(".next", "server", "app", "publications", "patent.html"),
    os.path.join(".next", "server", "app", "lectures.html"),
    os.path.join(".next", "server", "app", "standardization.html"),
    os.path.join(".next", "server", "app", "_not-found.html"),
    os.path.join(".next", "server", "app", "robots.txt.body"),
    os.path.join(".next", "server", "app", "sitemap.xml.body"),
    os.path.join(".next", "server", "pages", "404.html"),
]

def run_cmd(cmd, cwd=REFER_DIR):
    print(f"Running: {' '.join(cmd)} (cwd: {cwd})")
    res = subprocess.run(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    return res

def test_clean():
    print("\n--- Gate 0: Clean Step (workspace build cache purge) ---")
    res = run_cmd(["bash", "-c", "npm run clean"])
    if res.returncode != 0:
        print(f"❌ Clean step failed:\n{res.stdout}\n{res.stderr}")
        return False
    print("✅ Gate 0 PASSED: Cleaned .next and tsconfig.tsbuildinfo")
    return True

def test_lint():
    print("\n--- Gate 1: ESLint Quality Gate ---")
    res = run_cmd(["npm", "run", "lint"])
    if res.returncode != 0:
        print(f"❌ Lint failed:\n{res.stdout}\n{res.stderr}")
        return False
    print("✅ Gate 1 PASSED: ESLint clean (0 errors, 0 warnings)")
    return True

def test_build():
    print("\n--- Gate 2: Next.js Static SSG Build Gate ---")
    res = run_cmd(["npm", "run", "build"])
    if res.returncode != 0:
        print(f"❌ SSG Build failed:\n{res.stdout}\n{res.stderr}")
        return False
    
    # Check stdout/stderr for dynamic routes (f Dynamic)
    if "ƒ" in res.stdout or "Dynamic" in res.stdout:
        # Search specifically for dynamic routes output line
        lines = res.stdout.splitlines()
        dynamic_lines = [l for l in lines if "ƒ" in l or "Dynamic" in l]
        if dynamic_lines:
            print(f"❌ Found dynamic route(s) in build report: {dynamic_lines}")
            return False

    print("✅ Gate 2 PASSED: Static SSG Build successful (0 dynamic routes)")
    return True

def test_tsc():
    print("\n--- Gate 3: TypeScript Type Safety Gate ---")
    res = run_cmd(["npx", "tsc", "--noEmit"])
    if res.returncode != 0:
        print(f"❌ Type check failed:\n{res.stdout}\n{res.stderr}")
        return False
    print("✅ Gate 3 PASSED: TypeScript type check clean (exit code 0)")
    return True

def test_ssg_routes():
    print("\n--- Gate 4: Static SSG HTML Route Verification ---")
    missing = []
    for rel_path in EXPECTED_SSG_ROUTES:
        full_path = os.path.join(REFER_DIR, rel_path)
        if not os.path.exists(full_path):
            missing.append(rel_path)
        else:
            size = os.path.getsize(full_path)
            print(f"  [OK] {rel_path} ({size} bytes)")

    if missing:
        print(f"❌ Missing {len(missing)} SSG routes:")
        for m in missing:
            print(f"  - {m}")
        return False

    print(f"✅ Gate 4 PASSED: All {len(EXPECTED_SSG_ROUTES)} static SSG routes exist and are non-empty")
    return True

def test_data_counts():
    print("\n--- Gate 5: Data Losslessness Integrity Gate ---")
    script_path = os.path.join(REFER_DIR, "scripts", "extract", "verify_counts.py")
    res = run_cmd([sys.executable, script_path], cwd=ROOT_DIR)
    if res.returncode != 0:
        print(f"❌ Data count verification failed:\n{res.stdout}\n{res.stderr}")
        return False
    print(res.stdout.strip())
    print("✅ Gate 5 PASSED: Data count integrity & volume losslessness verified")
    return True

def test_theme_tokens():
    print("\n--- Gate 6: CSS & Theme Token Verification ---")
    globals_css = os.path.join(REFER_DIR, "app", "globals.css")
    tailwind_config = os.path.join(REFER_DIR, "tailwind.config.ts")
    
    if not os.path.exists(globals_css):
        print(f"❌ Missing {globals_css}")
        return False
    
    with open(globals_css, "r", encoding="utf-8") as f:
        css_text = f.read()

    tokens = ["--ink", "--ivory", "--paper", "--line", "--vermillion", "--cobalt"]
    missing_tokens = [t for t in tokens if t not in css_text]
    if missing_tokens:
        print(f"❌ Missing CSS theme tokens in globals.css: {missing_tokens}")
        return False

    with open(tailwind_config, "r", encoding="utf-8") as f:
        tw_text = f.read()

    tw_tokens = ["vermillion", "cobalt", "dark"]
    missing_tw = [t for t in tw_tokens if t not in tw_text]
    if missing_tw:
        print(f"❌ Missing Tailwind theme tokens in tailwind.config.ts: {missing_tw}")
        return False

    print("✅ Gate 6 PASSED: Light/Dark theme CSS variables and Tailwind tokens present")
    return True

def test_unittest_suite():
    print("\n--- Gate 7: DOM & HTML Content Assertions (Unittest) ---")
    test_path = os.path.join(REFER_DIR, "tests", "e2e_test_suite.py")
    res = run_cmd([sys.executable, "-m", "unittest", test_path], cwd=ROOT_DIR)
    if res.returncode != 0:
        print(f"❌ Unittest suite failed:\n{res.stdout}\n{res.stderr}")
        return False
    print("✅ Gate 7 PASSED: All 7 DOM & static HTML content assertion tests passed")
    return True

def test_layout_architecture():
    print("\n--- Gate 8: Layout Architecture & No-Escape Gate (AC-25 / AC-26 / R-8.17) ---")
    
    # 1) Check HTML SSG pages for container-content inside main
    routes_to_check = [
        os.path.join(".next", "server", "app", "index.html"),
        os.path.join(".next", "server", "app", "members.html"),
        os.path.join(".next", "server", "app", "publications.html"),
        os.path.join(".next", "server", "app", "lectures.html"),
        os.path.join(".next", "server", "app", "standardization.html"),
    ]
    
    for rel_p in routes_to_check:
        full_p = os.path.join(REFER_DIR, rel_p)
        if not os.path.exists(full_p):
            print(f"❌ Missing SSG route HTML: {rel_p}")
            return False
        with open(full_p, "r", encoding="utf-8") as f:
            html = f.read()
        
        # Verify <main tag does NOT contain max-w- or px-
        main_match = re.search(r'<main\s+class="([^"]*)"', html)
        if not main_match:
            print(f"❌ <main> tag not found in {rel_p}")
            return False
        main_cls = main_match.group(1)
        if "max-w-" in main_cls or "px-" in main_cls or "container-content" in main_cls:
            print(f"❌ <main> tag in {rel_p} restricts width or padding ({main_cls}). Should be w-full flex-1 only.")
            return False

        # Verify child wrapper has container-content
        if "container-content" not in html:
            print(f"❌ container-content missing in {rel_p}")
            return False

    # 2) Check source for forbidden escaped arbitrary classes (AC-26)
    forbidden_terms = ["w-screen", "-mx-[50vw]", "min-[1240px]:"]
    for root, _, files in os.walk(os.path.join(REFER_DIR, "app")):
        for fname in files:
            if fname.endswith((".ts", ".tsx")):
                fpath = os.path.join(root, fname)
                with open(fpath, "r", encoding="utf-8") as f:
                    content = f.read()
                for term in forbidden_terms:
                    if term in content:
                        print(f"❌ Forbidden term '{term}' found in {fpath}")
                        return False

    for root, _, files in os.walk(os.path.join(REFER_DIR, "components")):
        for fname in files:
            if fname.endswith((".ts", ".tsx")):
                fpath = os.path.join(root, fname)
                with open(fpath, "r", encoding="utf-8") as f:
                    content = f.read()
                for term in forbidden_terms:
                    if term in content:
                        print(f"❌ Forbidden term '{term}' found in {fpath}")
                        return False

    print("✅ Gate 8 PASSED: Layout 3-tier architecture and AC-25/AC-26 no-escape rule verified")
    return True

def main():
    print("=" * 70)
    print(" ANL HOMEPAGE E2E TEST SUITE RUNNER — TIERS 1-4")
    print("=" * 70)

    success = True
    success &= test_clean()
    success &= test_lint()
    success &= test_build()
    success &= test_tsc()
    success &= test_ssg_routes()
    success &= test_data_counts()
    success &= test_theme_tokens()
    success &= test_unittest_suite()
    success &= test_layout_architecture()

    print("\n" + "-" * 70)
    if success:
        print("ALL E2E TEST SUITES PASSED CLEAN (Exit Code 0).")
        print("=" * 70)
        sys.exit(0)
    else:
        print("❌ ONE OR MORE E2E GATES FAILED.")
        print("=" * 70)
        sys.exit(1)

if __name__ == "__main__":
    main()
