"""Compliance domain entities with coverage calculation logic.

Pure domain logic — no framework imports.
"""

# Enable future language features for compatibility
from __future__ import annotations

# Import enum
import enum

# Import uuid
import uuid

# Import dataclass, field from dataclasses
from dataclasses import dataclass, field


# Define class ControlCoverageStatus
class ControlCoverageStatus(str, enum.Enum):
    """Computed coverage level for a single compliance control."""

    # Assign covered = "covered"
    covered = "covered"
    # Assign partially_covered = "partially_covered"
    partially_covered = "partially_covered"
    # Assign not_covered = "not_covered"
    not_covered = "not_covered"


# Apply the @dataclass decorator
@dataclass
# Define class ComplianceControlEntity
class ComplianceControlEntity:
    """Pure domain representation of a single compliance framework control.

    Derives its coverage status from the technique statuses associated
    with it via the ``technique_statuses`` list.
    """

    # control_id: str
    control_id: str
    # title: str
    title: str
    # Assign id = None
    id: uuid.UUID | None = None
    # Assign description = None
    description: str | None = None
    # Assign category = None
    category: str | None = None
    # Assign technique_statuses = field(default_factory=list)
    technique_statuses: list[str] = field(default_factory=list)

    # Apply the @property decorator
    @property
    # Define function coverage_status
    def coverage_status(self) -> ControlCoverageStatus:
        """Compute the coverage status for this control based on linked technique statuses.

        Returns:
            ControlCoverageStatus: ``covered`` when all techniques are covered,
                ``partially_covered`` when at least one is covered, and
                ``not_covered`` when none are covered or the control has no techniques.
        """
        # Check: not self.technique_statuses
        if not self.technique_statuses:
            # Return ControlCoverageStatus.not_covered
            return ControlCoverageStatus.not_covered
        # Assign covered_statuses = {"validated", "partial"}
        covered_statuses = {"validated", "partial"}
        # Assign covered = [s for s in self.technique_statuses if s in covered_statuses]
        covered = [s for s in self.technique_statuses if s in covered_statuses]
        # Check: len(covered) == len(self.technique_statuses)
        if len(covered) == len(self.technique_statuses):
            # Return ControlCoverageStatus.covered
            return ControlCoverageStatus.covered
        # Alternative: len(covered) > 0
        elif len(covered) > 0:
            # Return ControlCoverageStatus.partially_covered
            return ControlCoverageStatus.partially_covered
        # Return ControlCoverageStatus.not_covered
        return ControlCoverageStatus.not_covered


# Apply the @dataclass decorator
@dataclass
# Define class ComplianceFrameworkEntity
class ComplianceFrameworkEntity:
    """Pure domain representation of a compliance framework (e.g. NIST 800-53, PCI-DSS).

    Aggregates a collection of controls and provides aggregate coverage statistics.
    """

    # name: str
    name: str
    # Assign id = None
    id: uuid.UUID | None = None
    # Assign version = None
    version: str | None = None
    # Assign description = None
    description: str | None = None
    # Assign is_active = True
    is_active: bool = True
    # Assign controls = field(default_factory=list)
    controls: list[ComplianceControlEntity] = field(default_factory=list)

    # Apply the @property decorator
    @property
    # Define function total_controls
    def total_controls(self) -> int:
        """Return the total number of controls in this framework.

        Returns:
            int: Count of all controls regardless of coverage status.
        """
        # Return len(self.controls)
        return len(self.controls)

    # Apply the @property decorator
    @property
    # Define function covered_controls
    def covered_controls(self) -> int:
        """Return the number of fully covered controls in this framework.

        Returns:
            int: Count of controls with ``ControlCoverageStatus.covered`` status.
        """
        # Return sum(
        return sum(
            # Literal argument value
            1 for c in self.controls
            if c.coverage_status == ControlCoverageStatus.covered
        )

    # Apply the @property decorator
    @property
    # Define function coverage_pct
    def coverage_pct(self) -> float:
        """Return the percentage of controls that are fully covered.

        Returns:
            float: A value from 0.0 to 100.0, rounded to one decimal place.
                Returns 0.0 when the framework has no controls.
        """
        # Check: self.total_controls == 0
        if self.total_controls == 0:
            # Return 0.0
            return 0.0
        # Return round(self.covered_controls / self.total_controls * 100, 1)
        return round(self.covered_controls / self.total_controls * 100, 1)

    # Define function get_gap_controls
    def get_gap_controls(self) -> list[ComplianceControlEntity]:
        """Return controls that are not fully covered.

        Returns:
            list[ComplianceControlEntity]: Controls with ``partially_covered`` or
                ``not_covered`` status.
        """
        # Return [
        return [
            c for c in self.controls
            if c.coverage_status != ControlCoverageStatus.covered
        ]
