# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Creature State

Persistence model for the Tamagotchi-style creature avatar.
Stored as JSON at ~/.familiar/data/creature.json using the same
atomic write-then-rename pattern as InteractionTracker.
"""

import json
import logging
import threading
from dataclasses import asdict, dataclass, field
from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Optional

logger = logging.getLogger(__name__)


class EvolutionStage(str, Enum):
    """Creature evolution stages, mapped to RelationshipStage."""

    BABY = "baby"  # STAGE_1: 0-10 interactions
    JUVENILE = "juvenile"  # STAGE_2: 10-50 interactions
    ADULT = "adult"  # STAGE_3: 50+ interactions


class ActivityState(str, Enum):
    """What the creature is currently doing."""

    IDLE = "idle"
    THINKING = "thinking"
    WORKING = "working"
    SLEEPING = "sleeping"


@dataclass
class CreatureState:
    """Persistent creature data."""

    name: str = ""
    species: str = "fox"
    color_body: str = "#4A90D9"
    color_eyes: str = "#FFD700"
    color_accent: str = "#FF6B6B"
    color_voice: str = "#e94560"
    evolution_stage: str = field(default=EvolutionStage.BABY.value)
    total_interactions: int = 0
    born_at: str = ""
    last_seen: str = ""
    last_evolution_at: str = ""
    owner_name: str = ""
    constitution_accepted: bool = False
    constitution_accepted_at: str = ""
    preferred_channel: str = ""
    job_class: str = ""


_lock = threading.Lock()


def _get_creature_file(data_dir: Optional[Path] = None) -> Path:
    """Get path to creature.json, respecting set_data_root()."""
    if data_dir is None:
        from ..core.paths import DATA_DIR

        data_dir = DATA_DIR
    return data_dir / "creature.json"


def load_creature(data_dir: Optional[Path] = None) -> Optional[CreatureState]:
    """Load creature state from disk. Returns None if no creature exists."""
    path = _get_creature_file(data_dir)
    if not path.exists():
        return None
    try:
        data = json.loads(path.read_text())
        return CreatureState(
            **{k: v for k, v in data.items() if k in CreatureState.__dataclass_fields__}
        )
    except (json.JSONDecodeError, IOError, TypeError) as e:
        logger.warning(f"Failed to load creature state: {e}")
        return None


def save_creature(creature: CreatureState, data_dir: Optional[Path] = None) -> None:
    """Persist creature state atomically (write-then-rename)."""
    path = _get_creature_file(data_dir)
    with _lock:
        try:
            path.parent.mkdir(parents=True, exist_ok=True)
            tmp = path.with_suffix(".json.tmp")
            tmp.write_text(json.dumps(asdict(creature), indent=2))
            tmp.replace(path)
        except IOError as e:
            logger.error(f"Failed to save creature state: {e}")


def create_creature(
    name: str,
    species: str = "fox",
    color_body: str = "#4A90D9",
    color_eyes: str = "#FFD700",
    color_accent: str = "#FF6B6B",
    color_voice: str = "#e94560",
    owner_name: str = "",
    constitution_accepted: bool = False,
    preferred_channel: str = "",
    data_dir: Optional[Path] = None,
) -> CreatureState:
    """Create a new creature and save to disk."""
    now = datetime.now().isoformat()
    creature = CreatureState(
        name=name,
        species=species,
        color_body=color_body,
        color_eyes=color_eyes,
        color_accent=color_accent,
        color_voice=color_voice,
        evolution_stage=EvolutionStage.BABY.value,
        total_interactions=0,
        born_at=now,
        last_seen=now,
        owner_name=owner_name,
        constitution_accepted=constitution_accepted,
        constitution_accepted_at=now if constitution_accepted else "",
        preferred_channel=preferred_channel,
    )
    save_creature(creature, data_dir)
    return creature
