"""
Projekt-Verwaltung — Stefans Idee fuer „Threads im Hauptchat verankert".

Ein Projekt ist ein benanntes Thema-Bündel. Zwei Modi:
  - Hauptthread (kein aktives Projekt): klassischer rollender Chat.
  - In-Projekt: alle neuen Turns werden mit project_id getaggt. Die App
    zeigt sie als zusammenhängenden Block, einklappbar.

Voice-Pattern (vom LLM via Meta-Tools getriggert):
  - „neues Projekt 'Aria-Wakeword'" → project_create
  - „steig in Projekt Spotify-Setup ein" → project_enter (Fuzzy-Match)
  - „Projekt Ende" → project_exit (zurueck zu Hauptthread)
  - „welche Projekte gibt's?" → project_list
  - „hol mich ab — was war zuletzt bei Projekt X?" → project_summary

Persistenz: JSON-Liste in /shared/config/projects.json + aktive ID
in /shared/config/active_project.txt. Single-User, single-active —
keine Concurrency-Probleme.
"""

from __future__ import annotations

import json
import logging
import os
import re
import time
import uuid
from difflib import SequenceMatcher
from pathlib import Path
from typing import Optional

logger = logging.getLogger(__name__)

PROJECTS_DIR = Path(os.environ.get("PROJECTS_DIR", "/shared/config"))
PROJECTS_FILE = PROJECTS_DIR / "projects.json"
ACTIVE_PROJECT_FILE = PROJECTS_DIR / "active_project.txt"


def _now() -> int:
    return int(time.time())


def _load_all() -> list[dict]:
    if not PROJECTS_FILE.exists():
        return []
    try:
        data = json.loads(PROJECTS_FILE.read_text(encoding="utf-8"))
        return data if isinstance(data, list) else []
    except Exception as exc:
        logger.warning("[projects] load failed: %s", exc)
        return []


def _save_all(projects: list[dict]) -> None:
    PROJECTS_DIR.mkdir(parents=True, exist_ok=True)
    PROJECTS_FILE.write_text(
        json.dumps(projects, indent=2, ensure_ascii=False), encoding="utf-8")


def _slug(name: str) -> str:
    """Stabile ID aus Namen — fuer Voice-Matches. Lowercase, only a-z 0-9 _."""
    s = name.strip().lower()
    s = re.sub(r"[^a-z0-9]+", "_", s)
    s = s.strip("_")
    return s or f"project_{_now()}"


def list_projects(include_archived: bool = False) -> list[dict]:
    projects = _load_all()
    if not include_archived:
        projects = [p for p in projects if p.get("status") != "archived"]
    projects.sort(key=lambda p: p.get("last_activity_at", 0), reverse=True)
    return projects


def get_project(project_id: str) -> Optional[dict]:
    if not project_id:
        return None
    for p in _load_all():
        if p.get("id") == project_id:
            return p
    return None


def find_project(query: str) -> Optional[dict]:
    """Fuzzy-Match auf Projekt-Namen — fuer Voice-Commands.
    Trifft auf: exact slug, prefix, substring, oder hoechste similarity > 0.6."""
    q = (query or "").strip().lower()
    if not q:
        return None
    projects = _load_all()
    # 1. Exact ID-Match
    for p in projects:
        if p.get("id") == q:
            return p
    # 2. Exact / Prefix / Substring auf Slug + Name
    q_slug = _slug(q)
    for p in projects:
        if p.get("id") == q_slug:
            return p
        name_low = (p.get("name", "")).lower()
        if name_low == q or name_low.startswith(q) or q in name_low:
            return p
    # 3. Fuzzy
    best, best_score = None, 0.0
    for p in projects:
        s = SequenceMatcher(None, q, p.get("name", "").lower()).ratio()
        if s > best_score:
            best, best_score = p, s
    if best and best_score >= 0.6:
        return best
    return None


def create_project(name: str, description: str = "") -> dict:
    name = (name or "").strip()
    if not name:
        raise ValueError("Projektname darf nicht leer sein")
    base_id = _slug(name)
    projects = _load_all()
    # Dedup by id with suffix
    used_ids = {p["id"] for p in projects}
    pid = base_id
    counter = 2
    while pid in used_ids:
        pid = f"{base_id}_{counter}"
        counter += 1
    now = _now()
    project = {
        "id": pid,
        "name": name,
        "description": description.strip(),
        "status": "active",  # active | ended | archived
        "created_at": now,
        "updated_at": now,
        "last_activity_at": now,
        "turn_count": 0,
    }
    projects.append(project)
    _save_all(projects)
    set_active(pid)
    logger.info("[projects] created %r (id=%s)", name, pid)
    return project


def update_project(project_id: str, patch: dict) -> Optional[dict]:
    projects = _load_all()
    for p in projects:
        if p["id"] == project_id:
            for k in ("name", "description", "status"):
                if k in patch and patch[k] is not None:
                    p[k] = patch[k]
            p["updated_at"] = _now()
            _save_all(projects)
            return p
    return None


def archive_project(project_id: str) -> bool:
    if update_project(project_id, {"status": "archived"}) is not None:
        if get_active() == project_id:
            set_active("")
        return True
    return False


def end_project(project_id: str) -> bool:
    """Markiert als beendet, aktive-Projekt-Pointer raus."""
    if update_project(project_id, {"status": "ended"}) is not None:
        if get_active() == project_id:
            set_active("")
        return True
    return False


def touch_project(project_id: str) -> None:
    """Bei jedem Turn im Projekt: last_activity + turn_count erhoehen."""
    if not project_id:
        return
    projects = _load_all()
    changed = False
    for p in projects:
        if p["id"] == project_id:
            p["last_activity_at"] = _now()
            p["turn_count"] = int(p.get("turn_count", 0)) + 1
            changed = True
            break
    if changed:
        _save_all(projects)


# ── Active-Project-Pointer ─────────────────────────────────────────

def get_active() -> str:
    """Returns die aktive Projekt-ID oder leer (= Hauptthread)."""
    try:
        if ACTIVE_PROJECT_FILE.exists():
            return ACTIVE_PROJECT_FILE.read_text(encoding="utf-8").strip()
    except Exception:
        pass
    return ""


def set_active(project_id: str) -> None:
    PROJECTS_DIR.mkdir(parents=True, exist_ok=True)
    ACTIVE_PROJECT_FILE.write_text(project_id or "", encoding="utf-8")
    logger.info("[projects] active project: %r", project_id or "(main)")


def status() -> dict:
    """Status-Snapshot fuer App/Diagnostic."""
    active_id = get_active()
    active = get_project(active_id) if active_id else None
    return {
        "active_id": active_id,
        "active": active,
        "projects": list_projects(include_archived=False),
    }
