#!/usr/bin/env python3
"""
HyperFrames composition linter — official + custom checks.

Runs `hyperframes lint --json` for structural checks, then supplements
with custom Python checks for patterns the official linter doesn't cover.

Usage:
    python3 scripts/hf-lint.py <project-dir>           # text output
    python3 scripts/hf-lint.py <project-dir> --json    # JSON output

Exit: 0 = clean (no errors), 1 = errors found
"""
import argparse
import importlib.util
import json
import os
import re
import subprocess
import sys

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
_lint_spec = importlib.util.spec_from_file_location(
    "lint_html", os.path.join(SCRIPT_DIR, "lint-html.py"))
_lint_html = importlib.util.module_from_spec(_lint_spec)
_lint_spec.loader.exec_module(_lint_html)


def run_official_lint(project_dir):
    """Run hyperframes lint --json, return parsed result or None."""
    try:
        result = subprocess.run(
            ["hyperframes", "lint", project_dir, "--json"],
            capture_output=True, text=True, timeout=30
        )
        if result.stdout.strip():
            return json.loads(result.stdout)
        return None
    except (subprocess.TimeoutExpired, FileNotFoundError, json.JSONDecodeError) as e:
        print(f"  WARNING: hyperframes lint failed: {e}", file=sys.stderr)
        return None


def run_custom_lint(html_path):
    """Run lint-html.py checks on an individual HTML file."""
    if not os.path.exists(html_path):
        return []
    with open(html_path, 'r') as f:
        html = f.read()
    return _lint_html.lint_html(html)


def lint_project(project_dir):
    """Run all lints and return combined findings."""
    all_findings = []

    # 1. Official linter
    official = run_official_lint(project_dir)
    if official and official.get("findings"):
        for f in official["findings"]:
            all_findings.append({
                "source": "hyperframes",
                "severity": f.get("severity", "error"),
                "code": f.get("code", "unknown"),
                "message": f.get("message", ""),
                "fixHint": f.get("fixHint", ""),
                "elementId": f.get("elementId"),
                "file": f.get("file", ""),
                "snippet": f.get("snippet", ""),
            })

    # 2. Custom checks on each HTML file in the project
    for root, dirs, files in os.walk(project_dir):
        for fname in files:
            if fname.endswith('.html') and not fname.startswith('.'):
                html_path = os.path.join(root, fname)
                custom_findings = run_custom_lint(html_path)
                for cf in custom_findings:
                    all_findings.append({
                        "source": "custom",
                        "severity": cf.get("severity", "error"),
                        "code": cf.get("code", "unknown"),
                        "message": cf.get("message", ""),
                        "fixHint": "",
                        "elementId": None,
                        "file": html_path,
                        "snippet": cf.get("snippet", ""),
                    })

    return all_findings


def format_findings(findings):
    """Human-readable output."""
    errors = [f for f in findings if f["severity"] == "error"]
    warnings = [f for f in findings if f["severity"] == "warning"]

    if not findings:
        print("  No issues found.")
        return

    print(f"  {len(errors)} error(s), {len(warnings)} warning(s)")
    print()

    for f in findings:
        prefix = "[FAIL]" if f["severity"] == "error" else "[WARN]"
        src = f"[{f['source']}]"
        print(f"{prefix} {src} {f['code']}: {f['message']}")
        if f.get("fixHint"):
            print(f"       Fix: {f['fixHint']}")
        if f.get("snippet"):
            print(f"       {f['snippet'][:150]}")

    if errors:
        print(f"\n  Fix all {len(errors)} error(s) above before rendering.")


def main():
    parser = argparse.ArgumentParser(
        description="HyperFrames lint — official + custom checks")
    parser.add_argument("project_dir", help="Project directory")
    parser.add_argument("--json", action="store_true", help="JSON output")
    args = parser.parse_args()

    project_dir = os.path.abspath(args.project_dir)
    if not os.path.isdir(project_dir):
        print(f"ERROR: {project_dir} is not a directory", file=sys.stderr)
        sys.exit(1)

    findings = lint_project(project_dir)

    if args.json:
        print(json.dumps({
            "ok": len([f for f in findings if f["severity"] == "error"]) == 0,
            "errorCount": len([f for f in findings if f["severity"] == "error"]),
            "warningCount": len([f for f in findings if f["severity"] == "warning"]),
            "findings": findings
        }, indent=2, ensure_ascii=False))
    else:
        print(f"=== HyperFrames Lint: {os.path.basename(project_dir)} ===")
        format_findings(findings)

    errors = sum(1 for f in findings if f["severity"] == "error")
    sys.exit(1 if errors > 0 else 0)


if __name__ == "__main__":
    main()
