"""
services/store.py
Simple async-safe JSON file store for scenarios and sessions.
Replace with a real database in production.
"""
from __future__ import annotations
import asyncio
import json
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Optional

from models.schemas import Scenario, ScenarioCreate, ScenarioUpdate, SessionState

_LOCK = asyncio.Lock()
_DATA_DIR = Path(__file__).parent.parent / "data"
_DATA_DIR.mkdir(exist_ok=True)

_SCENARIOS_FILE = _DATA_DIR / "scenarios.json"
_SESSIONS_FILE  = _DATA_DIR / "sessions.json"


# ── helpers ───────────────────────────────────────────────────────────────────

def _now() -> str:
    return datetime.now(timezone.utc).isoformat()

async def _read(path: Path) -> list:
    async with _LOCK:
        if not path.exists():
            return []
        return json.loads(path.read_text(encoding="utf-8"))

async def _write(path: Path, data: list) -> None:
    async with _LOCK:
        path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")


# ── Scenarios ─────────────────────────────────────────────────────────────────

async def list_scenarios(status: Optional[str] = None) -> List[Scenario]:
    rows = await _read(_SCENARIOS_FILE)
    if status:
        rows = [r for r in rows if r.get("status") == status]
    return [Scenario(**r) for r in rows]


async def get_scenario(sid: str) -> Optional[Scenario]:
    rows = await _read(_SCENARIOS_FILE)
    for r in rows:
        if r.get("id") == sid:
            return Scenario(**r)
    return None


async def create_scenario(body: ScenarioCreate) -> Scenario:
    rows = await _read(_SCENARIOS_FILE)
    record = Scenario(
        **body.model_dump(),
        id=str(uuid.uuid4()),
        status="draft",
        completions=0,
        avg_score=0.0,
        created_at=_now(),
        updated_at=_now(),
    )
    rows.insert(0, record.model_dump())
    await _write(_SCENARIOS_FILE, rows)
    return record


async def upsert_scenario(scenario: Scenario) -> Scenario:
    """Insert-or-replace a Scenario keyed on its `id`. Used by the external
    role-play bridge to persist scenarios that arrive inline from the browser
    so the existing WS code (which loads from the local store) can use them."""
    rows = await _read(_SCENARIOS_FILE)
    rows = [r for r in rows if r.get("id") != scenario.id]
    payload = scenario.model_dump()
    payload["updated_at"] = _now()
    if not payload.get("created_at"):
        payload["created_at"] = _now()
    rows.insert(0, payload)
    await _write(_SCENARIOS_FILE, rows)
    return scenario


async def update_scenario(sid: str, body: ScenarioUpdate) -> Optional[Scenario]:
    rows = await _read(_SCENARIOS_FILE)
    for i, r in enumerate(rows):
        if r.get("id") == sid:
            updated = {**r, **body.model_dump(), "id": sid, "updated_at": _now()}
            rows[i] = updated
            await _write(_SCENARIOS_FILE, rows)
            return Scenario(**updated)
    return None


async def patch_scenario(sid: str, patch: dict) -> Optional[Scenario]:
    rows = await _read(_SCENARIOS_FILE)
    for i, r in enumerate(rows):
        if r.get("id") == sid:
            updated = {**r, **patch, "id": sid, "updated_at": _now()}
            rows[i] = updated
            await _write(_SCENARIOS_FILE, rows)
            return Scenario(**updated)
    return None


async def delete_scenario(sid: str) -> bool:
    rows = await _read(_SCENARIOS_FILE)
    new = [r for r in rows if r.get("id") != sid]
    if len(new) == len(rows):
        return False
    await _write(_SCENARIOS_FILE, new)
    return True


# ── Sessions ──────────────────────────────────────────────────────────────────

async def save_session(session: SessionState) -> None:
    rows = await _read(_SESSIONS_FILE)
    # upsert
    rows = [r for r in rows if r.get("session_id") != session.session_id]
    rows.insert(0, session.model_dump())
    await _write(_SESSIONS_FILE, rows)


async def get_session(session_id: str) -> Optional[SessionState]:
    rows = await _read(_SESSIONS_FILE)
    for r in rows:
        if r.get("session_id") == session_id:
            return SessionState(**r)
    return None


async def list_sessions() -> list:
    return await _read(_SESSIONS_FILE)
