"""Core scanner — parses YAML, detects type, runs appropriate linters."""

from __future__ import annotations

from pathlib import Path

import yaml

from .analyzers import detect_file_type
from .analyzers.compose import lint_compose
from .analyzers.github_actions import lint_github_actions
from .analyzers.gitlab_ci import lint_gitlab_ci
from .analyzers.kubernetes import lint_kubernetes
from .analyzers.yaml_lint import lint_yaml
from .models import FileType, Grade, LintResult, ScanResult, Severity, grade_from_score


def scan_file(file_path: str) -> LintResult:
    """Scan a single YAML file."""
    result = LintResult(file_path=file_path)

    try:
        content = Path(file_path).read_text(encoding="utf-8")
    except FileNotFoundError:
        result.valid_yaml = False
        result.parse_error = f"File not found: {file_path}"
        result.score = 0
        result.grade = Grade.F
        return result
    except Exception as exc:
        result.valid_yaml = False
        result.parse_error = str(exc)
        result.score = 0
        result.grade = Grade.F
        return result

    return scan_content(content, file_path)


def scan_content(content: str, file_path: str = "<stdin>") -> LintResult:
    """Scan YAML content string."""
    result = LintResult(file_path=file_path)

    # Parse YAML
    try:
        docs = list(yaml.safe_load_all(content))
        docs = [d for d in docs if d is not None]
        result.documents = len(docs)
    except yaml.YAMLError as exc:
        result.valid_yaml = False
        result.parse_error = str(exc)
        result.score = 0
        result.grade = Grade.F
        return result

    if not docs:
        result.valid_yaml = True
        result.documents = 0
        result.score = 100
        result.grade = Grade.A_PLUS
        return result

    # Detect file type
    result.file_type = detect_file_type(content, file_path)

    # Run generic YAML lint
    findings = lint_yaml(content, file_path)

    # Run type-specific linter
    if result.file_type == FileType.KUBERNETES:
        findings.extend(lint_kubernetes(docs, file_path))
    elif result.file_type == FileType.DOCKER_COMPOSE:
        findings.extend(lint_compose(docs, file_path))
    elif result.file_type == FileType.GITHUB_ACTIONS:
        findings.extend(lint_github_actions(docs, file_path))
    elif result.file_type == FileType.GITLAB_CI:
        findings.extend(lint_gitlab_ci(docs, file_path))
    elif result.file_type == FileType.HELM:
        # Helm charts: apply K8s rules if they have apiVersion+kind
        if any(isinstance(d, dict) and d.get("kind") for d in docs):
            findings.extend(lint_kubernetes(docs, file_path))

    # Sort by severity
    sev_order = {Severity.CRITICAL: 0, Severity.HIGH: 1, Severity.MEDIUM: 2, Severity.LOW: 3, Severity.INFO: 4}
    findings.sort(key=lambda f: sev_order.get(f.rule.severity, 5))

    result.findings = findings

    # Calculate score
    score = 100
    for f in findings:
        if f.rule.severity == Severity.CRITICAL:
            score -= 15
        elif f.rule.severity == Severity.HIGH:
            score -= 8
        elif f.rule.severity == Severity.MEDIUM:
            score -= 3
        elif f.rule.severity == Severity.LOW:
            score -= 1
    result.score = max(0, min(100, score))
    result.grade = grade_from_score(result.score)

    return result


def scan_directory(
    directory: str,
    recursive: bool = True,
    extensions: tuple[str, ...] = (".yml", ".yaml"),
) -> ScanResult:
    """Scan a directory for YAML files."""
    scan = ScanResult()
    dir_path = Path(directory)

    if not dir_path.is_dir():
        return scan

    pattern = "**/*" if recursive else "*"
    files = []
    for ext in extensions:
        files.extend(dir_path.glob(f"{pattern}{ext}"))

    # Skip common directories
    skip_dirs = {".git", "node_modules", ".venv", "venv", "__pycache__", ".tox"}
    files = [
        f for f in files
        if not any(part in skip_dirs for part in f.parts)
    ]

    files.sort()
    scan.total_files = len(files)

    for fpath in files:
        result = scan_file(str(fpath))
        scan.files.append(result)
        scan.total_findings += len(result.findings)

    # Overall score
    if scan.files:
        scan.overall_score = sum(f.score for f in scan.files) // len(scan.files)
    else:
        scan.overall_score = 100
    scan.overall_grade = grade_from_score(scan.overall_score)

    return scan
