"""Main policy evaluation engine."""

from __future__ import annotations

import time
import uuid
from dataclasses import dataclass, field
from enum import Enum
from typing import TYPE_CHECKING, Any

from .parser import DiffParser, FileParser
from .reporter import EvaluationReport, Reporter, ReportFormat, Violation
from .rules import Rule, RuleEngine, RuleResult

if TYPE_CHECKING:
    from mergeguide.policies.loader import PolicyLoader


class InputType(Enum):
    """Type of input for evaluation."""

    FILE = "file"
    DIFF = "diff"
    CONTENT = "content"


@dataclass
class EvaluationContext:
    """Context for an evaluation run."""

    org_id: str = "default"
    repo: str = ""
    branch: str = ""
    commit_sha: str = ""
    author: str = ""
    pr_number: int | None = None
    metadata: dict[str, Any] = field(default_factory=dict)


@dataclass
class PolicyInput:
    """Input for policy evaluation."""

    input_type: InputType
    content: str
    file_path: str = ""
    context: EvaluationContext = field(default_factory=EvaluationContext)


@dataclass
class PolicyOutput:
    """Output from policy evaluation."""

    evaluation_id: str
    passed: bool
    violations: list[Violation]
    files_checked: list[str]
    duration_ms: int
    report: EvaluationReport

    def to_json(self) -> str:
        """Convert to JSON report."""
        reporter = Reporter()
        return reporter.format(self.report, ReportFormat.JSON)

    def to_sarif(self) -> str:
        """Convert to SARIF report."""
        reporter = Reporter()
        return reporter.format(self.report, ReportFormat.SARIF)


class PolicyEngine:
    """Main policy evaluation engine.

    The PolicyEngine is responsible for:
    1. Loading policies from various sources
    2. Parsing input (files, diffs, raw content)
    3. Executing rules against the input
    4. Generating evaluation reports
    """

    def __init__(
        self,
        rules: list[Rule] | None = None,
        policy_loader: PolicyLoader | None = None,
    ) -> None:
        """Initialize the policy engine.

        Args:
            rules: Pre-configured rules to use
            policy_loader: Loader for fetching policies from external sources
        """
        self.rule_engine = RuleEngine(rules or [])
        self.policy_loader = policy_loader
        self.diff_parser = DiffParser()
        self.file_parser = FileParser()
        self.reporter = Reporter()

    def add_rule(self, rule: Rule) -> None:
        """Add a rule to the engine."""
        self.rule_engine.add_rule(rule)

    def add_rules(self, rules: list[Rule]) -> None:
        """Add multiple rules to the engine."""
        for rule in rules:
            self.rule_engine.add_rule(rule)

    def load_policies(self, org_id: str = "default") -> None:
        """Load policies from the policy loader."""
        if self.policy_loader:
            rules = self.policy_loader.load_rules(org_id)
            self.add_rules(rules)

    def evaluate(self, input_data: PolicyInput) -> PolicyOutput:
        """Evaluate input against loaded policies.

        Args:
            input_data: The input to evaluate

        Returns:
            PolicyOutput with evaluation results
        """
        start_time = time.time()
        evaluation_id = f"eval-{uuid.uuid4().hex[:12]}"

        violations: list[Violation] = []
        files_checked: list[str] = []

        if input_data.input_type == InputType.FILE:
            parsed = self.file_parser.parse(input_data.content, input_data.file_path)
            results = self.rule_engine.check_file(parsed)
            violations = self._results_to_violations(results, input_data.file_path)
            files_checked = [input_data.file_path] if input_data.file_path else []

        elif input_data.input_type == InputType.DIFF:
            diff_files = self.diff_parser.parse(input_data.content)
            for diff_file in diff_files:
                if diff_file.is_binary:
                    continue
                results = self.rule_engine.check_diff(diff_file)
                file_violations = self._results_to_violations(results, diff_file.path)
                violations.extend(file_violations)
                files_checked.append(diff_file.path)

        elif input_data.input_type == InputType.CONTENT:
            parsed = self.file_parser.parse(input_data.content, input_data.file_path)
            results = self.rule_engine.check_file(parsed)
            violations = self._results_to_violations(results, input_data.file_path)
            files_checked = [input_data.file_path] if input_data.file_path else []

        duration_ms = int((time.time() - start_time) * 1000)

        report = EvaluationReport.create(
            evaluation_id=evaluation_id,
            violations=violations,
            files_checked=files_checked,
            duration_ms=duration_ms,
        )

        return PolicyOutput(
            evaluation_id=evaluation_id,
            passed=report.summary.passed,
            violations=violations,
            files_checked=files_checked,
            duration_ms=duration_ms,
            report=report,
        )

    def evaluate_file(self, file_path: str, content: str | None = None) -> PolicyOutput:
        """Convenience method to evaluate a single file.

        Args:
            file_path: Path to the file
            content: Optional file content (will read from disk if not provided)

        Returns:
            PolicyOutput with evaluation results
        """
        if content is None:
            with open(file_path) as f:
                content = f.read()

        return self.evaluate(
            PolicyInput(
                input_type=InputType.FILE,
                content=content,
                file_path=file_path,
            )
        )

    def evaluate_diff(
        self,
        diff: str,
        context: EvaluationContext | None = None,
    ) -> PolicyOutput:
        """Convenience method to evaluate a diff.

        Args:
            diff: Unified diff content
            context: Optional evaluation context

        Returns:
            PolicyOutput with evaluation results
        """
        return self.evaluate(
            PolicyInput(
                input_type=InputType.DIFF,
                content=diff,
                context=context or EvaluationContext(),
            )
        )

    def evaluate_content(
        self,
        content: str,
        file_path: str = "",
        context: EvaluationContext | None = None,
    ) -> PolicyOutput:
        """Convenience method to evaluate raw content.

        Args:
            content: Raw content to evaluate
            file_path: Optional file path for language detection
            context: Optional evaluation context

        Returns:
            PolicyOutput with evaluation results
        """
        return self.evaluate(
            PolicyInput(
                input_type=InputType.CONTENT,
                content=content,
                file_path=file_path,
                context=context or EvaluationContext(),
            )
        )

    def _results_to_violations(
        self,
        results: list[RuleResult],
        file_path: str,
    ) -> list[Violation]:
        """Convert rule results to violations."""
        violations = []
        for result in results:
            if not result.passed:
                for match in result.matches:
                    # Get rule from engine for additional info
                    rule = next(
                        (r for r in self.rule_engine.rules if r.id == result.rule_id),
                        None,
                    )
                    violations.append(
                        Violation(
                            rule_id=result.rule_id,
                            rule_name=rule.name if rule else result.rule_id,
                            message=result.message,
                            severity=result.severity.value,
                            file_path=file_path,
                            line_no=match.line_no,
                            column_start=match.column_start,
                            column_end=match.column_end,
                            matched_text=match.matched_text,
                            line_content=match.line_content,
                            remediation=rule.remediation if rule else "",
                        )
                    )
        return violations
