"""Project entity and related value objects for organizing meetings.

Projects provide grouping within workspaces with their own:
- Settings (export rules, trigger rules, templates)
- Members with ProjectRole-based permissions
- Configuration inheritance from workspace
"""


from __future__ import annotations

import re
from dataclasses import dataclass, field
from datetime import datetime
from uuid import UUID

from noteflow.domain.errors import CannotArchiveDefaultProjectError, ValidationError
from noteflow.domain.utils.time import utc_now
from noteflow.domain.value_objects import ExportFormat

# Slug validation pattern: lowercase alphanumeric with hyphens
SLUG_PATTERN = re.compile(r"^[a-z0-9-]+$")


@dataclass(frozen=True)
class ExportRules:
    """Project-level export configuration.

    None values indicate "inherit from parent" (workspace or system defaults).
    Set values override the parent configuration.

    Attributes:
        default_format: Default export format (markdown, html, pdf).
        include_audio: Whether to include audio file in exports.
        include_timestamps: Whether to include timestamps in transcript.
        template_id: ID of export template to use.
    """

    default_format: ExportFormat | None = None
    include_audio: bool | None = None
    include_timestamps: bool | None = None
    template_id: UUID | None = None


@dataclass(frozen=True)
class TriggerRules:
    """Project-level trigger configuration.

    None values indicate "inherit from parent" (workspace or system defaults).
    Empty list [] means "explicitly cleared" (override parent with empty).

    Attributes:
        auto_start_enabled: Whether auto-start recording is enabled.
        calendar_match_patterns: Glob patterns for calendar event titles.
        app_match_patterns: Glob patterns for application names.
    """

    auto_start_enabled: bool | None = None
    calendar_match_patterns: list[str] | None = None
    app_match_patterns: list[str] | None = None


@dataclass
class ProjectSettings:
    """Merged settings for a project.

    Combines export rules, trigger rules, and other configuration
    that can be inherited from workspace or overridden at project level.

    Attributes:
        export_rules: Export configuration.
        trigger_rules: Trigger configuration.
        rag_enabled: Whether RAG Q&A is enabled for this project.
        default_summarization_template: Default summarization template ID.
    """

    export_rules: ExportRules | None = None
    trigger_rules: TriggerRules | None = None
    rag_enabled: bool | None = None
    default_summarization_template: str | None = None


@dataclass(frozen=True)
class EffectiveRules:
    """Resolved rules after merging system defaults -> workspace -> project.

    All fields are guaranteed to have values (no None) after resolution.

    Attributes:
        export: Resolved export rules.
        trigger: Resolved trigger rules.
        rag_enabled: Resolved RAG enablement.
        default_summarization_template: Resolved template.
    """

    export: ExportRules
    trigger: TriggerRules
    rag_enabled: bool
    default_summarization_template: str | None


# System defaults used when neither workspace nor project specify a value
# System defaults used when neither workspace nor project specify a value
SYSTEM_DEFAULTS = EffectiveRules(
    export=ExportRules(
        default_format=ExportFormat.MARKDOWN,
        include_audio=False,
        include_timestamps=True,
        template_id=None,
    ),
    trigger=TriggerRules(
        auto_start_enabled=False,
        calendar_match_patterns=[],
        app_match_patterns=[],
    ),
    rag_enabled=False,
    default_summarization_template=None,
)


def slugify(name: str) -> str:
    """Convert a name to a valid slug.

    Args:
        name: The name to convert.

    Returns:
        Lowercase alphanumeric string with hyphens.
    """
    # Convert to lowercase, replace spaces/underscores with hyphens
    slug = name.lower().replace(" ", "-").replace("_", "-")
    # Remove any characters that aren't alphanumeric or hyphens
    slug = re.sub(r"[^a-z0-9-]", "", slug)
    # Collapse multiple hyphens
    slug = re.sub(r"-+", "-", slug)
    # Strip leading/trailing hyphens
    return slug.strip("-")


@dataclass
class Project:
    """A project for organizing meetings and artifacts within a workspace.

    Projects provide:
    - Grouping of related meetings and artifacts
    - Project-level configuration (export rules, trigger rules, templates)
    - Fine-grained access control with ProjectRole
    - Foundation for RAG Q&A scoped to project knowledge

    Constraints:
    - slug must be unique per-workspace when set (enforced via DB constraint)
    - slug must match pattern [a-z0-9-]+
    - is_default=True projects cannot be archived
    - Each workspace has exactly one default project

    Attributes:
        id: Unique project identifier.
        workspace_id: Parent workspace identifier.
        name: User-provided project name.
        slug: URL-friendly identifier (unique per workspace).
        description: Optional project description.
        is_default: Whether this is the workspace's default project.
        settings: Project-level settings and rules.
        created_at: Project creation timestamp.
        updated_at: Last modification timestamp.
        archived_at: Archive timestamp (None if active).
        metadata: Extensible metadata dictionary.
    """

    id: UUID
    workspace_id: UUID
    name: str
    slug: str | None = None
    description: str | None = None
    is_default: bool = False
    settings: ProjectSettings = field(default_factory=ProjectSettings)
    created_at: datetime = field(default_factory=utc_now)
    updated_at: datetime = field(default_factory=utc_now)
    archived_at: datetime | None = None
    metadata: dict[str, object] = field(default_factory=dict)

    def __post_init__(self) -> None:
        """Validate project state after initialization."""
        if self.slug is not None and not SLUG_PATTERN.match(self.slug):
            raise ValidationError(
                "slug",
                f"must match pattern [a-z0-9-]+, got: {self.slug!r}",
            )

    @property
    def is_archived(self) -> bool:
        """Check if the project is archived.

        Returns:
            True if archived_at is set.
        """
        return self.archived_at is not None

    @property
    def is_active(self) -> bool:
        """Check if the project is active (not archived).

        Returns:
            True if not archived.
        """
        return self.archived_at is None

    def archive(self) -> None:
        """Mark project as archived.

        Raises:
            CannotArchiveDefaultProjectError: If this is a default project.
        """
        if self.is_default:
            raise CannotArchiveDefaultProjectError(str(self.id))
        self.archived_at = utc_now()
        self.updated_at = utc_now()

    def restore(self) -> None:
        """Restore an archived project."""
        self.archived_at = None
        self.updated_at = utc_now()

    def update_name(self, name: str) -> None:
        """Update the project name.

        Args:
            name: New project name.
        """
        self.name = name
        self.updated_at = utc_now()

    def update_slug(self, slug: str | None) -> None:
        """Update the project slug.

        Validate the slug format before assignment.

        Args:
            slug: New project slug (or None to clear).

        Raises:
            ValidationError: If slug format is invalid.
        """
        if slug is not None and not SLUG_PATTERN.match(slug):
            raise ValidationError(
                "slug",
                f"must match pattern [a-z0-9-]+, got: {slug!r}",
            )
        self.slug = slug
        self.updated_at = utc_now()

    def update_description(self, description: str | None) -> None:
        """Update the project description.

        Args:
            description: New project description.
        """
        self.description = description
        self.updated_at = utc_now()

    def update_settings(self, settings: ProjectSettings) -> None:
        """Update the project settings.

        Args:
            settings: New project settings.
        """
        self.settings = settings
        self.updated_at = utc_now()

    def set_metadata(self, key: str, value: object) -> None:
        """Set a metadata value.

        Args:
            key: Metadata key.
            value: Metadata value.
        """
        self.metadata[key] = value
        self.updated_at = utc_now()
