"""Append-only JSONL session logger for the workspace learning loop.

Log path: projects/{project}/sessions/{YYYY-MM-DD}.jsonl
One file per day. Each line is a JSON object with:
- timestamp (ISO 8601)
- type (feedback, action, prime, query, generation_submitted, generation_completed)
- shot_id (optional)
- take_id (optional)
- provenance_hash (optional — from take's inputs_snapshot if available)
- data (type-specific payload)

Categories for feedback entries:
- pattern: recurring issue across shots ("kling-v3 always under-lights interiors")
- correction: specific fix ("this shot needs tighter framing")
- observation: notable finding ("the window reflection looks great here")
- preference: JT taste signal ("I prefer the warmer grade")
- trivial: low-value, don't synthesize ("ok" / "looks fine")

NOTE: generation_completed entries will be written by a file watcher in v2,
when actual generation execution is connected. The POC only logs generation_submitted.
"""

import json
import os
import threading
from datetime import datetime, timezone, date
from pathlib import Path
from typing import Any, Optional

_LOCK = threading.Lock()


def _now_iso() -> str:
    return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")


def _log_path(project: str, projects_root: Path) -> Path:
    """Return today's session log path for the project."""
    today = date.today().isoformat()  # YYYY-MM-DD
    return projects_root / project / "sessions" / f"{today}.jsonl"


def append_entry(
    project: str,
    projects_root: Path,
    entry_type: str,
    *,
    shot_id: Optional[str] = None,
    take_id: Optional[str] = None,
    provenance_hash: Optional[str] = None,
    data: Optional[dict[str, Any]] = None,
) -> dict:
    """Append an entry to today's session log. Returns the written record."""
    log_path = _log_path(project, projects_root)
    log_path.parent.mkdir(parents=True, exist_ok=True)

    record = {
        "timestamp": _now_iso(),
        "type": entry_type,
    }
    if shot_id is not None:
        record["shot_id"] = shot_id
    if take_id is not None:
        record["take_id"] = take_id
    if provenance_hash is not None:
        record["provenance_hash"] = provenance_hash
    if data is not None:
        record["data"] = data

    line = json.dumps(record, separators=(",", ":")) + "\n"
    with _LOCK:
        with log_path.open("a", encoding="utf-8") as f:
            f.write(line)
            f.flush()

    return record


def read_entries(
    project: str,
    projects_root: Path,
    since: Optional[str] = None,
) -> list[dict]:
    """Read session log entries, optionally filtering by timestamp.

    Args:
        project: Project name.
        projects_root: Path to projects root directory.
        since: ISO 8601 timestamp. Only return entries after this time.

    Returns:
        List of log entries (dicts), newest last.
    """
    log_path = _log_path(project, projects_root)
    if not log_path.is_file():
        return []

    entries = []
    with log_path.open(encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                record = json.loads(line)
            except (json.JSONDecodeError, TypeError):
                continue
            if since is not None:
                ts = record.get("timestamp", "")
                if ts and ts <= since:
                    continue
            entries.append(record)

    return entries


def read_all_session_files(
    project: str,
    projects_root: Path,
    limit: int = 200,
) -> list[dict]:
    """Read entries across all session files for a project (most recent first).

    Used for context recovery after compaction. Returns at most `limit` entries.
    """
    sessions_dir = projects_root / project / "sessions"
    if not sessions_dir.is_dir():
        return []

    # Sort session files by name (YYYY-MM-DD.jsonl sorts chronologically)
    files = sorted(sessions_dir.glob("*.jsonl"), reverse=True)

    entries = []
    for session_file in files:
        if len(entries) >= limit:
            break
        try:
            with session_file.open(encoding="utf-8") as f:
                file_entries = []
                for line in f:
                    line = line.strip()
                    if not line:
                        continue
                    try:
                        file_entries.append(json.loads(line))
                    except (json.JSONDecodeError, TypeError):
                        continue
                # Add in reverse so newest entries come first
                for entry in reversed(file_entries):
                    entries.append(entry)
                    if len(entries) >= limit:
                        break
        except IOError:
            continue

    return entries
