"""Checker for security checks gated behind NODE_ENV conditions."""

import re
from pathlib import Path
from typing import List

from stablestack.checkers.base import BaseChecker
from stablestack.models import Finding, Severity, Tier


class NodeEnvSecurityGateChecker(BaseChecker):
    """Detects security logic that only runs in production via NODE_ENV guards."""

    rule_id = "SEC012"
    rule_name = "node-env-security-gate"
    description = "Security checks should not be gated behind NODE_ENV conditions"
    why = (
        "Wrapping security checks in `if (NODE_ENV === 'production')` means they are "
        "skipped in development, staging, and test environments. Attackers can exploit "
        "staging/preview deployments where NODE_ENV isn't 'production', and developers "
        "never test the security code path locally. Security checks should always run."
    )
    severity = Severity.ERROR
    tier = Tier.CRITICAL
    file_extensions = {".js", ".ts", ".jsx", ".tsx"}

    # Patterns: NODE_ENV guards around security-sensitive operations
    _PATTERNS = [
        # if (NODE_ENV === "production" && secret) or if (NODE_ENV === "production") { verify
        (
            re.compile(
                r'if\s*\(\s*(?:process\.env\.)?NODE_ENV\s*===?\s*[\'"]production[\'"]\s*'
                r'(?:&&\s*\w*(?:secret|key|token|signature)\w*)\s*\)',
                re.IGNORECASE,
            ),
            "Security check only runs when NODE_ENV is 'production'",
        ),
        # if (NODE_ENV !== "production") return/skip — bypasses security in non-prod
        (
            re.compile(
                r'if\s*\(\s*(?:process\.env\.)?NODE_ENV\s*!==?\s*[\'"]production[\'"]\s*\)\s*'
                r'(?:\{[^}]*)?(?:return|continue|next\(\))',
                re.IGNORECASE | re.DOTALL,
            ),
            "Security logic skipped when NODE_ENV is not 'production'",
        ),
        # NODE_ENV === "production" ? verifySignature() : skipVerification()
        (
            re.compile(
                r'(?:process\.env\.)?NODE_ENV\s*===?\s*[\'"]production[\'"]\s*\?\s*'
                r'\w*(?:verify|auth|check|validate|encrypt|secure)\w*',
                re.IGNORECASE,
            ),
            "Security function conditionally called based on NODE_ENV",
        ),
        # const skipAuth = NODE_ENV !== "production"
        (
            re.compile(
                r'(?:const|let|var)\s+\w*(?:skip|bypass|disable)\w*(?:Auth|Security|Verify|Check)\w*'
                r'\s*=\s*(?:process\.env\.)?NODE_ENV\s*!==?\s*[\'"]production[\'"]',
                re.IGNORECASE,
            ),
            "Security bypass flag derived from NODE_ENV",
        ),
        # if (!isProd) { /* skip verification */ }  where isProd = NODE_ENV === "production"
        (
            re.compile(
                r'if\s*\(\s*!?\s*(?:is_?prod(?:uction)?)\s*\)\s*\{[^}]*'
                r'(?:return|skip|bypass|next\(\))',
                re.IGNORECASE | re.DOTALL,
            ),
            "Security logic guarded by isProd/isProduction flag",
        ),
        # NODE_ENV check directly wrapping cron/webhook secret validation
        (
            re.compile(
                r'if\s*\(\s*(?:process\.env\.)?NODE_ENV\s*===?\s*[\'"]production[\'"]\s*\)\s*\{[^}]*'
                r'(?:cron|webhook|secret|signature|token|hmac|verify)',
                re.IGNORECASE | re.DOTALL,
            ),
            "Cron/webhook security verification only runs in production",
        ),
    ]

    def should_skip_file(self, path: Path) -> bool:
        """Skip test files and config files."""
        if "test" in path.stem.lower() or "spec" in path.stem.lower():
            return True
        if "mock" in path.stem.lower():
            return True
        return super().should_skip_file(path)

    def check_file(self, path: Path, content: str) -> List[Finding]:
        findings = []
        lines = content.splitlines()

        for pattern, description in self._PATTERNS:
            for match in pattern.finditer(content):
                line_num = content[: match.start()].count("\n") + 1
                line_content = lines[line_num - 1] if line_num <= len(lines) else ""

                # Skip comments
                stripped = line_content.lstrip()
                if stripped.startswith("//") or stripped.startswith("/*"):
                    continue

                findings.append(
                    self.create_finding(
                        path=path,
                        line=line_num,
                        column=match.start() - content.rfind("\n", 0, match.start()),
                        code=line_content.strip()[:80]
                        + ("..." if len(line_content.strip()) > 80 else ""),
                        message=f"NODE_ENV security gate: {description}",
                        fix_hint=self._get_fix_hint(),
                    )
                )

        return findings

    def _get_fix_hint(self) -> str:
        return (
            "Security checks should ALWAYS run, regardless of environment:\n\n"
            "BAD — security only in production:\n"
            '  if (process.env.NODE_ENV === "production") {\n'
            "    verifyWebhookSignature(req);\n"
            "  }\n\n"
            "GOOD — always verify:\n"
            "  verifyWebhookSignature(req);\n\n"
            "If you need different behavior per environment, use separate config,\n"
            "not conditional security bypasses. For local development, use test\n"
            "secrets rather than skipping verification entirely."
        )
