"""
Nuclear Criticality Safety Standards Lookup

Provides lookup capabilities for ANSI/ANS-8 series standards
and other nuclear criticality safety references.
"""

import logging
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any

logger = logging.getLogger(__name__)


class StandardType(str, Enum):
    """Type of safety standard."""

    ANSI_ANS = "ansi_ans"
    NRC_REG = "nrc_reg"
    DOE_STD = "doe_std"
    ISO = "iso"


class StandardStatus(str, Enum):
    """Publication status of a standard."""

    CURRENT = "current"
    SUPERSEDED = "superseded"
    WITHDRAWN = "withdrawn"
    DRAFT = "draft"


@dataclass
class StandardRevision:
    """Revision history entry for a standard."""

    version: str
    year: int
    status: StandardStatus
    superseded_by: str | None = None
    key_changes: list[str] = field(default_factory=list)


@dataclass
class SubcriticalLimit:
    """A single subcritical limit from a standard."""

    parameter: str  # e.g., "mass", "concentration"
    value: float
    unit: str
    material: str
    form: str  # metal, solution, oxide
    reflection: str  # bare, water, concrete
    moderation: str | None = None  # dry, optimally moderated
    conditions: list[str] = field(default_factory=list)
    notes: str = ""


@dataclass
class StandardInfo:
    """Information about a safety standard."""

    standard_id: str  # e.g., "ANSI/ANS-8.1-2014"
    title: str
    standard_type: StandardType
    status: StandardStatus
    year: int
    description: str = ""
    scope: str = ""
    revisions: list[StandardRevision] = field(default_factory=list)
    limits: list[SubcriticalLimit] = field(default_factory=list)
    related_standards: list[str] = field(default_factory=list)


class StandardsLookup:
    """
    Lookup tool for nuclear criticality safety standards.

    Provides access to subcritical limits and requirements from
    ANSI/ANS-8 series and related standards.

    Example:
        lookup = StandardsLookup()

        # Look up specific standard
        std = lookup.lookup("ANSI/ANS-8.1-2014")

        # Get limits for a material
        limits = lookup.get_limits(material="U-235", form="metal")

        # Check compliance
        is_compliant = lookup.check_compliance(
            standard_id="ANSI/ANS-8.1-2014",
            parameter="mass",
            value=10.0,
            material="U-235",
            form="metal"
        )
    """

    # Standards database
    STANDARDS_DB: dict[str, StandardInfo] = {
        "ANSI/ANS-8.1-2014": StandardInfo(
            standard_id="ANSI/ANS-8.1-2014",
            title="Nuclear Criticality Safety in Operations with Fissionable Materials Outside Reactors",
            standard_type=StandardType.ANSI_ANS,
            status=StandardStatus.CURRENT,
            year=2014,
            description="Provides guidance for criticality safety in handling fissionable materials",
            scope="Operations with fissionable materials outside reactors",
            revisions=[
                StandardRevision("R2014", 2014, StandardStatus.CURRENT),
                StandardRevision("1998", 1998, StandardStatus.SUPERSEDED, superseded_by="R2014"),
                StandardRevision("1983", 1983, StandardStatus.SUPERSEDED, superseded_by="1998"),
            ],
            limits=[
                SubcriticalLimit("mass", 20.1, "kg", "U-235", "metal", "bare"),
                SubcriticalLimit("mass", 22.8, "kg", "U-235", "metal", "water"),
                SubcriticalLimit("mass", 0.78, "kg", "U-235", "solution", "bare"),
                SubcriticalLimit("mass", 0.81, "kg", "U-235", "solution", "water"),
                SubcriticalLimit("mass", 5.0, "kg", "Pu-239", "metal", "bare"),
                SubcriticalLimit("mass", 5.5, "kg", "Pu-239", "metal", "water"),
                SubcriticalLimit("mass", 0.51, "kg", "Pu-239", "solution", "bare"),
                SubcriticalLimit("concentration", 11.6, "g/L", "U-235", "solution", "bare"),
                SubcriticalLimit("concentration", 7.3, "g/L", "Pu-239", "solution", "bare"),
                SubcriticalLimit("volume", 5.8, "L", "U-235", "solution", "bare"),
            ],
            related_standards=[
                "ANSI/ANS-8.3",
                "ANSI/ANS-8.7",
                "ANSI/ANS-8.12",
            ],
        ),
        "ANSI/ANS-8.3-1997": StandardInfo(
            standard_id="ANSI/ANS-8.3-1997",
            title="Criticality Accident Alarm System",
            standard_type=StandardType.ANSI_ANS,
            status=StandardStatus.CURRENT,
            year=1997,
            description="Requirements for criticality accident alarm systems",
            scope="Design and operation of criticality alarm systems",
            revisions=[
                StandardRevision("R2017", 2017, StandardStatus.CURRENT),
                StandardRevision("1997", 1997, StandardStatus.SUPERSEDED),
            ],
            related_standards=["ANSI/ANS-8.1", "ANSI/ANS-8.23"],
        ),
        "ANSI/ANS-8.7-1998": StandardInfo(
            standard_id="ANSI/ANS-8.7-1998",
            title="Nuclear Criticality Safety in the Storage of Fissile Materials",
            standard_type=StandardType.ANSI_ANS,
            status=StandardStatus.CURRENT,
            year=1998,
            description="Guidance for safe storage of fissile materials",
            scope="Storage of fissile materials in arrays",
            revisions=[
                StandardRevision("R2017", 2017, StandardStatus.CURRENT),
                StandardRevision("1998", 1998, StandardStatus.SUPERSEDED),
            ],
            related_standards=["ANSI/ANS-8.1", "ANSI/ANS-8.12"],
        ),
        "ANSI/ANS-8.12-1987": StandardInfo(
            standard_id="ANSI/ANS-8.12-1987",
            title="Nuclear Criticality Control and Safety of Plutonium-Uranium Fuel Mixtures Outside Reactors",
            standard_type=StandardType.ANSI_ANS,
            status=StandardStatus.CURRENT,
            year=1987,
            description="Criticality control for Pu-U fuel mixtures",
            scope="Mixed oxide fuel handling and storage",
            limits=[
                SubcriticalLimit("mass", 6.0, "kg", "Pu-U", "oxide", "bare", notes="MOX fuel"),
            ],
            related_standards=["ANSI/ANS-8.1"],
        ),
    }

    def __init__(self) -> None:
        """Initialize the standards lookup tool."""
        logger.info("Initialized Standards Lookup tool")
        self._last_update = datetime.now()

    def lookup(self, standard_id: str) -> StandardInfo | None:
        """
        Look up a standard by ID.

        Args:
            standard_id: Standard identifier (e.g., "ANSI/ANS-8.1-2014")

        Returns:
            StandardInfo or None if not found
        """
        # Try exact match first
        if standard_id in self.STANDARDS_DB:
            return self.STANDARDS_DB[standard_id]

        # Try partial match (without year)
        base_id = standard_id.rsplit("-", 1)[0] if "-" in standard_id else standard_id
        for sid, info in self.STANDARDS_DB.items():
            if sid.startswith(base_id):
                return info

        return None

    def get_requirements(
        self,
        standard_id: str,
    ) -> dict[str, Any]:
        """
        Get requirements from a standard.

        Args:
            standard_id: Standard identifier

        Returns:
            Dictionary of requirements
        """
        info = self.lookup(standard_id)
        if not info:
            return {}

        return {
            "standard_id": info.standard_id,
            "title": info.title,
            "scope": info.scope,
            "limits": [
                {
                    "parameter": lim.parameter,
                    "value": lim.value,
                    "unit": lim.unit,
                    "material": lim.material,
                    "form": lim.form,
                    "reflection": lim.reflection,
                }
                for lim in info.limits
            ],
            "related_standards": info.related_standards,
        }

    def get_limits(
        self,
        material: str | None = None,
        form: str | None = None,
        parameter: str | None = None,
    ) -> list[SubcriticalLimit]:
        """
        Get subcritical limits matching criteria.

        Args:
            material: Filter by material (e.g., "U-235")
            form: Filter by physical form (e.g., "metal")
            parameter: Filter by parameter type (e.g., "mass")

        Returns:
            List of matching SubcriticalLimit objects
        """
        results = []

        for info in self.STANDARDS_DB.values():
            for limit in info.limits:
                if material and limit.material != material:
                    continue
                if form and limit.form != form:
                    continue
                if parameter and limit.parameter != parameter:
                    continue
                results.append(limit)

        return results

    def check_compliance(
        self,
        standard_id: str,
        parameter: str,
        value: float,
        material: str,
        form: str,
        reflection: str = "bare",
    ) -> tuple[bool, str]:
        """
        Check if a value complies with standard limits.

        Args:
            standard_id: Standard to check against
            parameter: Parameter type (mass, concentration, etc.)
            value: Value to check
            material: Fissile material
            form: Physical form
            reflection: Reflection condition

        Returns:
            Tuple of (compliant, message)
        """
        info = self.lookup(standard_id)
        if not info:
            return False, f"Standard not found: {standard_id}"

        # Find applicable limit
        applicable_limits = [
            lim for lim in info.limits
            if lim.parameter == parameter
            and lim.material == material
            and lim.form == form
            and lim.reflection == reflection
        ]

        if not applicable_limits:
            return True, f"No specific limit found for {material} {form} {parameter}"

        limit = applicable_limits[0]

        if value <= limit.value:
            margin = ((limit.value - value) / limit.value) * 100
            return True, f"Compliant: {value} {limit.unit} <= {limit.value} {limit.unit} (margin: {margin:.1f}%)"
        else:
            excess = ((value - limit.value) / limit.value) * 100
            return False, f"Non-compliant: {value} {limit.unit} > {limit.value} {limit.unit} (excess: {excess:.1f}%)"

    def get_current_version(self, standard_id: str) -> str | None:
        """
        Get the current version of a standard.

        Args:
            standard_id: Standard identifier (may be outdated)

        Returns:
            Current standard ID or None
        """
        info = self.lookup(standard_id)
        if not info:
            return None

        if info.status == StandardStatus.CURRENT:
            return info.standard_id

        # Check revisions for current
        for rev in info.revisions:
            if rev.status == StandardStatus.CURRENT:
                base = standard_id.rsplit("-", 1)[0]
                return f"{base}-{rev.version}"

        return info.standard_id

    def list_standards(
        self,
        standard_type: StandardType | None = None,
        status: StandardStatus | None = None,
    ) -> list[str]:
        """
        List available standards.

        Args:
            standard_type: Filter by type
            status: Filter by status

        Returns:
            List of standard IDs
        """
        results = []
        for sid, info in self.STANDARDS_DB.items():
            if standard_type and info.standard_type != standard_type:
                continue
            if status and info.status != status:
                continue
            results.append(sid)
        return sorted(results)
