"""
Identity Disc - Carries a Program's memory and decisions.

"Everything you do or learn will be imprinted on this disc."
"""

from dataclasses import dataclass, field
from datetime import datetime
from typing import Optional, Any
from enum import Enum
import json
import uuid


class DiscStatus(Enum):
    """Status of an Identity Disc."""
    ACTIVE = "active"
    ARCHIVED = "archived"
    DEREZZED = "derezzed"


@dataclass
class Decision:
    """A recorded decision made by a Program."""
    timestamp: datetime
    choice: str
    reasoning: str
    alternatives: list[str] = field(default_factory=list)

    def to_dict(self) -> dict:
        return {
            "timestamp": self.timestamp.isoformat(),
            "choice": self.choice,
            "reasoning": self.reasoning,
            "alternatives": self.alternatives,
        }


@dataclass
class Discovery:
    """Something learned during execution."""
    timestamp: datetime
    finding: str
    source: str
    relevance: str = "medium"  # low, medium, high, critical

    def to_dict(self) -> dict:
        return {
            "timestamp": self.timestamp.isoformat(),
            "finding": self.finding,
            "source": self.source,
            "relevance": self.relevance,
        }


@dataclass
class Artifact:
    """A file or resource created/modified by a Program."""
    path: str
    action: str  # created, modified, deleted
    timestamp: datetime
    description: str = ""

    def to_dict(self) -> dict:
        return {
            "path": self.path,
            "action": self.action,
            "timestamp": self.timestamp.isoformat(),
            "description": self.description,
        }


class IdentityDisc:
    """
    The Identity Disc - carries a Program's memory and decisions.

    Each Program on The Grid carries an Identity Disc containing:
    - Purpose: What the Program is trying to accomplish
    - Constraints: Boundaries it must respect
    - Decisions: Choices made and why
    - Discoveries: Things learned during execution
    - Artifacts: Files created/modified
    - Lineage: Parent/child relationships for fission
    """

    def __init__(
        self,
        program_id: str,
        purpose: str = "",
        parent_disc: Optional["IdentityDisc"] = None,
    ):
        self.id = str(uuid.uuid4())[:8]
        self.program_id = program_id
        self.created_at = datetime.now()
        self.status = DiscStatus.ACTIVE

        # Core identity
        self.purpose = purpose
        self.constraints: list[str] = []

        # Memory
        self.decisions: list[Decision] = []
        self.discoveries: list[Discovery] = []
        self.artifacts: list[Artifact] = []

        # Lineage (for fission)
        self.parent_disc = parent_disc
        self.child_discs: list["IdentityDisc"] = []
        self.depth = 0 if parent_disc is None else parent_disc.depth + 1

        # Energy tracking
        self.energy_allocated = 0
        self.energy_consumed = 0

        # Inherit relevant context from parent
        if parent_disc:
            self._inherit_from_parent(parent_disc)

    def _inherit_from_parent(self, parent: "IdentityDisc") -> None:
        """Inherit relevant context from parent disc."""
        # Inherit constraints
        self.constraints = parent.constraints.copy()

        # Inherit high-relevance discoveries
        for discovery in parent.discoveries:
            if discovery.relevance in ("high", "critical"):
                self.discoveries.append(discovery)

        # Register as child of parent
        parent.child_discs.append(self)

    def record_decision(
        self,
        choice: str,
        reasoning: str,
        alternatives: list[str] = None,
    ) -> Decision:
        """Record a decision made by the Program."""
        decision = Decision(
            timestamp=datetime.now(),
            choice=choice,
            reasoning=reasoning,
            alternatives=alternatives or [],
        )
        self.decisions.append(decision)
        return decision

    def record_discovery(
        self,
        finding: str,
        source: str,
        relevance: str = "medium",
    ) -> Discovery:
        """Record something learned during execution."""
        discovery = Discovery(
            timestamp=datetime.now(),
            finding=finding,
            source=source,
            relevance=relevance,
        )
        self.discoveries.append(discovery)
        return discovery

    def record_artifact(
        self,
        path: str,
        action: str,
        description: str = "",
    ) -> Artifact:
        """Record a file/resource created or modified."""
        artifact = Artifact(
            path=path,
            action=action,
            timestamp=datetime.now(),
            description=description,
        )
        self.artifacts.append(artifact)
        return artifact

    def add_constraint(self, constraint: str) -> None:
        """Add a constraint the Program must respect."""
        if constraint not in self.constraints:
            self.constraints.append(constraint)

    def consume_energy(self, amount: int) -> bool:
        """Consume energy. Returns False if insufficient energy."""
        if self.energy_consumed + amount > self.energy_allocated:
            return False
        self.energy_consumed += amount
        return True

    def energy_remaining(self) -> int:
        """Get remaining energy."""
        return self.energy_allocated - self.energy_consumed

    def energy_percentage(self) -> float:
        """Get energy as percentage of allocated."""
        if self.energy_allocated == 0:
            return 0.0
        return (self.energy_remaining() / self.energy_allocated) * 100

    def derez(self) -> None:
        """Mark disc as derezzed (completed/archived)."""
        self.status = DiscStatus.DEREZZED

    def get_lineage(self) -> list[str]:
        """Get the lineage chain of program IDs."""
        lineage = [self.program_id]
        current = self.parent_disc
        while current:
            lineage.insert(0, current.program_id)
            current = current.parent_disc
        return lineage

    def to_dict(self) -> dict:
        """Serialize disc to dictionary."""
        return {
            "id": self.id,
            "program_id": self.program_id,
            "created_at": self.created_at.isoformat(),
            "status": self.status.value,
            "purpose": self.purpose,
            "constraints": self.constraints,
            "decisions": [d.to_dict() for d in self.decisions],
            "discoveries": [d.to_dict() for d in self.discoveries],
            "artifacts": [a.to_dict() for a in self.artifacts],
            "depth": self.depth,
            "lineage": self.get_lineage(),
            "energy_allocated": self.energy_allocated,
            "energy_consumed": self.energy_consumed,
            "child_count": len(self.child_discs),
        }

    def to_json(self) -> str:
        """Serialize disc to JSON."""
        return json.dumps(self.to_dict(), indent=2)

    def summary(self) -> str:
        """Get a brief summary of the disc contents."""
        lines = [
            f"Identity Disc [{self.id}]",
            f"  Program: {self.program_id}",
            f"  Purpose: {self.purpose}",
            f"  Depth: {self.depth}",
            f"  Energy: {self.energy_remaining()}/{self.energy_allocated}",
            f"  Decisions: {len(self.decisions)}",
            f"  Discoveries: {len(self.discoveries)}",
            f"  Artifacts: {len(self.artifacts)}",
            f"  Children: {len(self.child_discs)}",
        ]
        return "\n".join(lines)

    def __repr__(self) -> str:
        return f"IdentityDisc(id={self.id}, program={self.program_id}, depth={self.depth})"
