"""S15-S20: Console frontend integrity checks."""

import os
import re

from recoil_checks import register_check


def check_tab_js_files(base, discovered):
    """S15: Console tab JS files exist and are syntactically valid."""
    passes, fails, warns = [], [], []

    tabs_dir = os.path.join(base, "editors", "tabs")
    if not os.path.isdir(tabs_dir):
        warns.append("editors/tabs/ directory not found")
        return {"pass": passes, "fail": fails, "warn": warns}

    for name in sorted(os.listdir(tabs_dir)):
        if not name.endswith(".js"):
            continue
        path = os.path.join(tabs_dir, name)
        with open(path, encoding="utf-8") as f:
            content = f.read()

        # Basic JS syntax: check balanced braces
        opens = content.count("{")
        closes = content.count("}")
        if abs(opens - closes) > 2:
            warns.append(f"{name}: unbalanced braces ({opens} open, {closes} close)")
        else:
            passes.append(f"{name}: exists and braces balanced")

    return {"pass": passes, "fail": fails, "warn": warns}


def check_console_app_js(base, discovered):
    """S16: console_app.js exists and has tab initialization."""
    passes, fails, warns = [], [], []

    app_path = os.path.join(base, "editors", "console_app.js")
    if not os.path.isfile(app_path):
        warns.append("console_app.js not found")
        return {"pass": passes, "fail": fails, "warn": warns}

    with open(app_path, encoding="utf-8") as f:
        content = f.read()

    passes.append("console_app.js exists")

    # Check for tab switching logic
    if "tab" in content.lower() and ("click" in content.lower() or "addEventListener" in content.lower()):
        passes.append("Tab switching logic present")
    else:
        warns.append("No tab switching logic detected")

    return {"pass": passes, "fail": fails, "warn": warns}


def check_onclick_targets(base, discovered):
    """S17: onclick targets in HTML reference existing functions."""
    passes, fails, warns = [], [], []

    console_path = os.path.join(base, "editors", "production-console.html")
    if not os.path.isfile(console_path):
        warns.append("production-console.html not found")
        return {"pass": passes, "fail": fails, "warn": warns}

    with open(console_path, encoding="utf-8") as f:
        html = f.read()

    # Find onclick="funcName(...)" references
    onclick_funcs = set()
    for m in re.finditer(r'onclick=["\'](\w+)\s*\(', html):
        onclick_funcs.add(m.group(1))

    if not onclick_funcs:
        passes.append("No onclick handlers to verify")
        return {"pass": passes, "fail": fails, "warn": warns}

    # Collect all JS content
    all_js = html  # inline scripts
    for js_file in ["console_app.js"]:
        path = os.path.join(base, "editors", js_file)
        if os.path.isfile(path):
            with open(path, encoding="utf-8") as f:
                all_js += f.read()

    tabs_dir = os.path.join(base, "editors", "tabs")
    if os.path.isdir(tabs_dir):
        for name in os.listdir(tabs_dir):
            if name.endswith(".js"):
                with open(os.path.join(tabs_dir, name), encoding="utf-8") as f:
                    all_js += f.read()

    for func in sorted(onclick_funcs):
        if f"function {func}" in all_js or f"{func} =" in all_js or f"{func}=" in all_js:
            passes.append(f"onclick target '{func}' defined")
        else:
            warns.append(f"onclick target '{func}' may not be defined")

    return {"pass": passes, "fail": fails, "warn": warns}


def check_morphdom_vendored(base, discovered):
    """S18: morphdom library is vendored (if used)."""
    passes, fails, warns = [], [], []

    lib_dir = os.path.join(base, "editors", "lib")
    editors_dir = os.path.join(base, "editors")

    # Check if morphdom is referenced
    all_content = ""
    for root, _, files in os.walk(editors_dir):
        for f in files:
            if f.endswith((".js", ".html")):
                with open(os.path.join(root, f), encoding="utf-8") as fh:
                    all_content += fh.read()

    if "morphdom" not in all_content:
        passes.append("morphdom not used (no reference found)")
        return {"pass": passes, "fail": fails, "warn": warns}

    # morphdom is referenced — check vendored
    morphdom_path = os.path.join(lib_dir, "morphdom-umd.min.js")
    if os.path.isfile(morphdom_path):
        passes.append("morphdom vendored at editors/lib/morphdom-umd.min.js")
    else:
        # Check other possible locations
        found = False
        for root, _, files in os.walk(editors_dir):
            for f in files:
                if "morphdom" in f.lower():
                    passes.append(f"morphdom found at {os.path.relpath(os.path.join(root, f), base)}")
                    found = True
                    break
            if found:
                break
        if not found:
            fails.append("morphdom referenced but not vendored")

    return {"pass": passes, "fail": fails, "warn": warns}


def check_escape_html(base, discovered):
    """S19: escapeHTML is applied where user content is rendered."""
    passes, fails, warns = [], [], []

    editors_dir = os.path.join(base, "editors")
    # Only flag template literals (backticks) — these interpolate dynamic data.
    # Static strings ('...' or "...") can't contain XSS vectors.
    dangerous_patterns = [
        (r'innerHTML\s*=\s*`', "innerHTML with template literal"),
        (r'\.innerHTML\s*\+=', "innerHTML concatenation"),
    ]

    for root, _, files in os.walk(editors_dir):
        for f in files:
            if not f.endswith(".js"):
                continue
            # Skip archived files
            if "_archive" in root:
                continue
            path = os.path.join(root, f)
            with open(path, encoding="utf-8") as fh:
                content = fh.read()

            # Check innerHTML concatenation (always single-line)
            concat_matches = re.findall(r'\.innerHTML\s*\+=', content)
            if concat_matches:
                warns.append(f"{f}: {len(concat_matches)} innerHTML concatenation")

            # For template literals, extract the full block and check interpolations
            unescaped_blocks = 0
            for m in re.finditer(r'innerHTML\s*=\s*`', content):
                start = m.end()
                # Find closing backtick (not escaped)
                depth = 0
                pos = start
                while pos < len(content):
                    ch = content[pos]
                    if ch == '\\':
                        pos += 2
                        continue
                    if ch == '`' and depth == 0:
                        break
                    if ch == '{' and pos > 0 and content[pos - 1] == '$':
                        depth += 1
                    elif ch == '}' and depth > 0:
                        depth -= 1
                    pos += 1
                block = content[start:pos]
                # Find ${...} interpolations that don't use escapeHTML
                interps = re.findall(r'\$\{([^}]+)\}', block)
                for interp in interps:
                    # Skip safe patterns: numbers, booleans, lengths, computed CSS,
                    # function calls (render functions build escaped HTML internally)
                    stripped = interp.strip()
                    if re.match(r'^[\d.+\-*/() ]+$', stripped):
                        continue
                    # Numeric expressions: var + N, var || N, var.prop || N
                    if re.match(r'^[\w.]+\s*[+\-*/|]+\s*[\d]+$', stripped):
                        continue
                    # Simple identifiers likely holding numbers (ep, idx, count)
                    if re.match(r'^[a-z]\w{0,5}$', stripped):
                        continue
                    # Function calls like renderFoo(...) return pre-built HTML
                    if re.match(r'^[a-zA-Z_]\w*\(', stripped):
                        continue
                    # Short identifiers ending in Color/Style are CSS values
                    if re.match(r'^[a-z]\w*(Color|Style|Class)$', stripped):
                        continue
                    if any(safe in interp for safe in [
                        'escapeHTML', 'esc(', '.length', '.size',
                        '.toFixed', '.toUpperCase', '? ', 'var(--',
                        '.join(', '.map(',
                    ]):
                        continue
                    unescaped_blocks += 1
                    break  # One unescaped interp per block is enough

            if unescaped_blocks:
                warns.append(f"{f}: {unescaped_blocks} innerHTML template(s) with unescaped interpolation")

    if not warns:
        passes.append("All innerHTML template literals use escapeHTML")

    return {"pass": passes, "fail": fails, "warn": warns}


def check_console_html_tabs(base, discovered):
    """S20: Console HTML references all tab modules."""
    passes, fails, warns = [], [], []

    console_path = os.path.join(base, "editors", "production-console.html")
    tabs_dir = os.path.join(base, "editors", "tabs")

    if not os.path.isfile(console_path):
        warns.append("production-console.html not found")
        return {"pass": passes, "fail": fails, "warn": warns}

    with open(console_path, encoding="utf-8") as f:
        html = f.read()

    if not os.path.isdir(tabs_dir):
        warns.append("editors/tabs/ directory not found")
        return {"pass": passes, "fail": fails, "warn": warns}

    for name in sorted(os.listdir(tabs_dir)):
        if not name.endswith(".js"):
            continue
        if name in html or f"tabs/{name}" in html:
            passes.append(f"Tab module {name} referenced in production-console.html")
        else:
            warns.append(f"Tab module {name} not referenced in production-console.html")

    return {"pass": passes, "fail": fails, "warn": warns}


register_check("s15_tab_js", "Tab JS Files", check_tab_js_files, section="frontend")
register_check("s16_console_app", "Console App JS", check_console_app_js, section="frontend")
register_check("s17_onclick_targets", "onclick Targets", check_onclick_targets, section="frontend")
register_check("s18_morphdom", "morphdom Vendored", check_morphdom_vendored, section="frontend")
register_check("s19_escape_html", "XSS: escapeHTML", check_escape_html, section="frontend")
register_check("s20_console_tabs", "Console Tab References", check_console_html_tabs, section="frontend")
