"""
Configuration file security analyzer

Scans configuration files (.env, Dockerfile, k8s, terraform, etc.) for
security issues, secrets, and misconfigurations using OpenGrep.
"""
import json
import logging
import os
import subprocess
from pathlib import Path
from typing import Any, Dict, List, Optional

from fastapi import APIRouter, Depends, HTTPException
from fastapi.concurrency import run_in_threadpool

from app.auth import get_current_user
from app.models import User
from app.database import SessionDep
from app.models import ScanRecord, ScanType
from app.utils.path_security import validate_and_normalize_path

logger = logging.getLogger(__name__)

router = APIRouter()


def save_config_scan_to_db(
    session, target_path: str, scan_data: Dict[str, Any], user_id: str
) -> str:
    """Save a config scan result to the database"""
    scan_record = ScanRecord(
        target=target_path,
        scan_type=ScanType.CONFIG,
        scan_data=scan_data,
        user_id=user_id,
    )
    session.add(scan_record)
    session.commit()
    session.refresh(scan_record)
    return scan_record.id


def detect_config_type(file_path: str) -> Optional[str]:
    """Detect configuration file type

    Returns:
        Config type: 'secrets', 'docker', 'kubernetes', 'terraform', 'yaml', 'json', etc.
    """
    ext = Path(file_path).suffix.lower()
    filename = Path(file_path).name.lower()

    # Environment files (secrets) - match .env and .env.*
    if filename.startswith(".env") or ext == ".env":
        return "secrets"

    # Docker files - match Dockerfile* patterns
    if filename.startswith("dockerfile") or filename == ".dockerignore":
        return "docker"
    if filename.startswith("docker-compose") and ext in [".yml", ".yaml"]:
        return "docker"

    # Kubernetes files
    if ext in [".yaml", ".yml"] and any(
        k in filename for k in ["k8s", "kube", "deployment", "service", "ingress"]
    ):
        return "kubernetes"

    # Terraform files
    if ext in [".tf", ".tfvars"]:
        return "terraform"

    # Generic config files
    if ext in [".yaml", ".yml"]:
        return "yaml"
    if ext in [".json"]:
        return "json"
    if ext in [".toml"]:
        return "toml"
    if ext in [".ini", ".cfg", ".conf"]:
        return "config"
    if ext in [".xml"]:
        return "xml"
    if ext in [".properties"]:
        return "properties"

    return None


def get_rules_for_config(config_type: str) -> List[str]:
    """Get scanner rules for configuration file type

    Args:
        config_type: Type of config file (secrets, docker, kubernetes, etc.)

    Returns:
        List of scanner rule configs to use
    """
    rules_map = {
        "secrets": ["p/secrets", "p/security-audit"],
        "docker": ["p/docker", "p/security-audit"],
        "kubernetes": ["p/kubernetes", "p/security-audit"],
        "terraform": ["p/terraform", "p/security-audit"],
        "yaml": ["p/secrets", "p/security-audit"],
        "json": ["p/secrets", "p/security-audit"],
        "toml": ["p/secrets", "p/security-audit"],
        "config": ["p/secrets", "p/security-audit"],
        "xml": ["p/security-audit"],
        "properties": ["p/secrets", "p/security-audit"],
    }

    return rules_map.get(config_type, ["p/security-audit"])


def run_config_scan_sync(
    target_path: str, session: SessionDep, user_id: str
) -> Dict[str, Any]:
    """Synchronous config file scan execution

    Args:
        target_path: Path to config file to scan
        session: Database session for storing results
        user_id: User who initiated the scan

    Returns:
        Scan results with findings

    Raises:
        ValueError: If path is invalid or file type not supported
    """
    # Security: Validate and normalize path
    target_path = validate_and_normalize_path(target_path)

    # Detect config file type
    config_type = detect_config_type(target_path)
    if not config_type:
        raise ValueError(
            f"Unsupported config file type: {Path(target_path).suffix}. "
            "Supported: .env, Dockerfile, docker-compose.yml, k8s/*.yaml, *.tf, "
            ".json, .toml, .ini, .cfg, .conf, .xml, .properties"
        )

    logger.info(f"Detected config type: {config_type} for {target_path}")

    # Get appropriate opengrep rules
    rules = get_rules_for_config(config_type)

    # Build opengrep command
    from app.code_analyzer.engine import _opengrep_exe
    cmd = [_opengrep_exe(), "scan", "--json", "--quiet"]
    for rule in rules:
        cmd.extend(["--config", rule])
    cmd.append(target_path)

    # Run opengrep
    logger.info(f"Running config scan: {' '.join(cmd)}")
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)

    # Parse results
    scan_results = json.loads(result.stdout) if result.stdout else {}
    findings = scan_results.get("results", [])

    logger.info(
        f"Config scan completed for {target_path}: {len(findings)} findings "
        f"(type: {config_type}, rules: {', '.join(rules)})"
    )

    # Prepare scan data
    scan_data = {
        "success": True,
        "config_type": config_type,
        "rules_used": rules,
        "findings": findings,
        "findings_count": len(findings),
        "errors": scan_results.get("errors", []),
        "paths_scanned": scan_results.get("paths", {}).get("scanned", []),
    }

    # Save to database
    if session:
        try:
            save_config_scan_to_db(session, target_path, scan_data, user_id=user_id)
            logger.info(f"Saved config scan results to database")
        except Exception as e:
            logger.error(f"Failed to save config scan to database: {e}")

    return scan_results


@router.post("/config-scan")
async def config_scan(
    target_path: str,
    session: SessionDep = None,
    user: User = Depends(get_current_user),
):
    """
    Scan configuration files for security issues and secrets

    Supports: .env files, Dockerfiles, docker-compose.yml, Kubernetes manifests,
    Terraform files, JSON, YAML, TOML, INI, XML, and .properties files.

    Uses OpenGrep with specialized rulesets for each config type to detect:
    - Hardcoded secrets and credentials
    - Security misconfigurations
    - Insecure settings
    - Best practice violations

    Args:
        target_path: Path to configuration file to scan
        session: Database session for storing results
        user: Authenticated user from JWT token

    Returns:
        Scan results with findings
    """
    try:
        logger.info(f"Starting config scan for: {target_path}")

        # Run the scan
        results = await run_in_threadpool(
            run_config_scan_sync, target_path, session, user.id
        )

        return {"success": True, "results": results}
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except subprocess.TimeoutExpired:
        raise HTTPException(
            status_code=504, detail="Config scan timed out after 5 minutes"
        )
    except Exception as e:
        logger.exception(f"Failed to run config scan for {target_path}")
        raise HTTPException(status_code=500, detail=f"Scan failed: {str(e)}")
