"""On-disk CRUD for project metadata."""

__author__ = "Lene Preuss <lene.preuss@gmail.com>"

import json
import re
from pathlib import Path

from stable_delusion.exceptions.project_error import ProjectError
from stable_delusion.exceptions.project_not_found_error import ProjectNotFoundError
from stable_delusion.project.models import ProjectMetadata


def _slugify(name: str) -> str:
    slug = name.lower().strip()
    slug = re.sub(r"[^a-z0-9]+", "-", slug)
    return slug.strip("-")


class ProjectRepository:
    """On-disk CRUD for project metadata."""

    def create(self, projects_root: Path, name: str, metadata: ProjectMetadata) -> Path:
        """Create a new project directory with project.json and standard subdirs."""
        slug = _slugify(name)
        project_dir = projects_root / slug
        if project_dir.exists():
            raise ProjectError(
                f"Project directory already exists: {project_dir}", project_name=name
            )
        project_dir.mkdir(parents=True)
        (project_dir / "sources").mkdir()
        (project_dir / "scenes").mkdir()
        (project_dir / "output").mkdir()
        self._write(project_dir, metadata)
        return project_dir

    def get(self, project_dir: Path) -> ProjectMetadata:
        """Read project.json from project directory."""
        json_path = project_dir / "project.json"
        if not json_path.exists():
            raise ProjectNotFoundError(project_dir.name, project_dir.parent)
        return ProjectMetadata.from_dict(json.loads(json_path.read_text(encoding="utf-8")))

    def update(self, project_dir: Path, metadata: ProjectMetadata) -> None:
        """Write updated project.json."""
        self._write(project_dir, metadata)

    def list_projects(self, projects_root: Path) -> list[tuple[Path, ProjectMetadata]]:
        """List all projects in projects_root, sorted by directory name."""
        if not projects_root.exists():
            return []
        results = []
        for entry in sorted(projects_root.iterdir()):
            if entry.is_dir() and (entry / "project.json").exists():
                results.append((entry, self.get(entry)))
        return results

    def find_by_name(self, projects_root: Path, name: str) -> tuple[Path, ProjectMetadata]:
        """Find project by slug or exact name match. Raises ProjectNotFoundError if absent."""
        slug = _slugify(name)
        candidate = projects_root / slug
        if candidate.exists() and (candidate / "project.json").exists():
            return candidate, self.get(candidate)
        for path, meta in self.list_projects(projects_root):
            if meta.name == name:
                return path, meta
        raise ProjectNotFoundError(name, projects_root)

    def _write(self, project_dir: Path, metadata: ProjectMetadata) -> None:
        json_path = project_dir / "project.json"
        json_path.write_text(json.dumps(metadata.to_dict(), indent=2), encoding="utf-8")
