"""Project management CLI commands: creating, removing, and listing projects."""

import shutil
from pathlib import Path
from typing import Annotated, Optional

import typer
import yaml
from pydantic import BaseModel
from rich.console import Console
from rich.table import Table
from slugify import slugify

from notulist.config import AppSettings
from notulist.editor import launch_editor


class ProjectMetadata(BaseModel):
    """YAML frontmatter metadata for a project."""

    name: str


class Project:
    """A project with metadata, description, and directory path."""

    def __init__(self, metadata: ProjectMetadata, description: str, path: Path):
        """
        Initialize a Project.

        Parameters
        ----------
        metadata : ProjectMetadata
            The project's frontmatter metadata.
        description : str
            The project description body.
        path : Path
            The project directory path.
        """
        self.metadata = metadata
        self.description = description
        self.path = path

    @property
    def slug(self) -> str:
        """Return the project slug (directory name)."""
        return self.path.name

    @staticmethod
    def load(project_dir: Path) -> "Project":
        """
        Load a project from its directory by parsing PROJECT.md.

        Parameters
        ----------
        project_dir : Path
            Path to the project directory containing PROJECT.md.

        Returns
        -------
        Project
            The parsed project.
        """
        project_file = project_dir / "PROJECT.md"
        text = project_file.read_text()
        parts = text.split("---", 2)
        metadata = ProjectMetadata.model_validate(yaml.safe_load(parts[1]))
        description = parts[2].strip()
        return Project(metadata=metadata, description=description, path=project_dir)


def slugify_name(name: str) -> str:
    """
    Generate a URL-friendly slug from a project name.

    Parameters
    ----------
    name : str
        The human-readable project name.

    Returns
    -------
    str
        A lowercase, hyphen-separated slug.
    """
    return slugify(name)


projects_app = typer.Typer()


@projects_app.command()
def create(
    name: Annotated[str, typer.Option("-n", "--name", help="Project name")],
    description: Annotated[
        Optional[str],
        typer.Option("-d", "--description", help="Project description"),
    ] = None,
):
    """Create a new project in the notes repository."""
    if description is None:
        description = launch_editor()

    if not description:
        print("Error: project description is required.")
        raise typer.Exit(1)

    settings = AppSettings()
    repository = Path(settings.repository)
    slug = slugify_name(name)
    project_dir = repository / "projects" / slug

    if project_dir.exists():
        print(f"Error: project directory already exists: {project_dir}")
        raise typer.Exit(1)

    project_dir.mkdir(parents=True)

    metadata = ProjectMetadata(name=name)
    frontmatter = yaml.dump(
        metadata.model_dump(mode="json"), default_flow_style=False
    ).strip()

    project_file = project_dir / "PROJECT.md"
    project_file.write_text(f"---\n{frontmatter}\n---\n\n{description}\n")

    print(project_file)


@projects_app.command()
def remove(
    name: Annotated[str, typer.Option("-n", "--name", help="Project slug")],
):
    """Remove a project from the notes repository."""
    settings = AppSettings()
    repository = Path(settings.repository)
    project_dir = repository / "projects" / name

    if not project_dir.exists():
        print(f"Error: project '{name}' not found.")
        raise typer.Exit(1)

    notes = [f for f in project_dir.glob("*.md") if f.name != "PROJECT.md"]
    if notes:
        confirmed = typer.confirm(f"Project '{name}' contains notes. Remove anyway?")
        if not confirmed:
            raise typer.Exit(1)

    shutil.rmtree(project_dir)
    print(f"Removed project: {project_dir}")


@projects_app.command("ls")
def ls():
    """List all projects in the notes repository."""
    settings = AppSettings()
    projects_dir = Path(settings.repository) / "projects"

    if not projects_dir.exists():
        print(
            "No projects found. Use `notulist projects create` to create a new project"
        )
        return

    table = Table(title="Projects")
    table.add_column("Slug")
    table.add_column("Name")
    table.add_column("Notes", justify="right")

    has_projects = False
    for project_dir in sorted(projects_dir.iterdir()):
        if not project_dir.is_dir() or not (project_dir / "PROJECT.md").exists():
            continue

        project = Project.load(project_dir)
        note_count = len(
            [f for f in project_dir.glob("*.md") if f.name != "PROJECT.md"]
        )

        table.add_row(project.slug, project.metadata.name, str(note_count))
        has_projects = True

    if not has_projects:
        print(
            "No projects found. Use `notulist projects create` to create a new project"
        )
        return

    console = Console()
    console.print(table)
