"""
Page seed schemas from pages.yaml.

PageSeeds define the default pages that MUST exist for each tenant/site.
These are the minimum viable pages for a functioning site.

SST File: lightwave/schema/definitions/pages.yaml
Database Model: sites.Page

NOTE: Page types and statuses are defined in field_options.yaml, not hardcoded here.
Layout names come from layouts.yaml. Use validators for runtime validation.
"""

from pathlib import Path
from typing import Any, ClassVar

import yaml
from pydantic import Field, field_validator, model_validator

from lightwave.schema.core.paths import get_sst_path
from lightwave.schema.pydantic.core.base import SSTSchema
from lightwave.schema.pydantic.core.validators import FieldOptions


class PageMetadataSchema(SSTSchema):
    """
    SEO and social metadata for a page.

    Used for <head> meta tags and social sharing.
    """

    title: str | None = Field(None, description="Custom <title> tag (falls back to page title)")
    description: str | None = Field(None, description="Meta description for SEO")
    og_image: str | None = Field(None, description="Open Graph image URL")
    robots: str | None = Field(None, description="Robots directive (e.g., 'noindex')")
    canonical_url: str | None = Field(None, description="Canonical URL if different from path")


class PageSeedSchema(SSTSchema):
    """
    Page seed schema from pages.yaml.

    Defines default pages that must exist for each tenant.
    Pages defined here are created during reconciliation if missing.

    Example:
        page = PageSeedSchema(
            path="/pricing/",
            title="Pricing",
            slug="pricing",
            page_type="pricing",
            blueprint="pricing",
            layout="web",
        )

    SST Validation:
        pages = PageSeedSchema.get_all_for_site("cineos.io")
        page = PageSeedSchema.get_by_path("cineos.io", "/pricing/")
    """

    SST_KEY: ClassVar[str] = "pages"  # Registry key in __index.yaml

    path: str = Field(..., description="URL path (e.g., '/', '/pricing/')")
    title: str = Field(..., description="Page title for display and <title> tag")
    slug: str = Field("", description="URL slug (derived from path if not provided)")
    page_type: str = Field("content", description="Page type for categorization")
    blueprint: str | None = Field(None, description="PageSchema name for data validation")
    layout: str = Field("web", description="Layout name (web, auth, app, etc.)")
    status: str = Field("published", description="Publication status")
    data: dict[str, Any] = Field(default_factory=dict, description="Page-specific structured data")
    metadata: PageMetadataSchema | dict[str, Any] | None = Field(None, description="SEO and social metadata")
    requires_auth: bool = Field(False, description="Whether page requires authentication")

    @field_validator("page_type")
    @classmethod
    def validate_page_type(cls, v: str) -> str:
        """Validate page_type against field_options.yaml."""
        return FieldOptions.validate("page_types", v)

    @field_validator("status")
    @classmethod
    def validate_status(cls, v: str) -> str:
        """Validate status against field_options.yaml."""
        return FieldOptions.validate("page_statuses", v)

    @field_validator("layout")
    @classmethod
    def validate_layout(cls, v: str) -> str:
        """Validate layout against layouts.yaml."""
        # Import here to avoid circular imports
        from lightwave.schema.pydantic.models.layouts import LayoutSchema

        return LayoutSchema.validate_layout_name(v)

    @model_validator(mode="after")
    def validate_path(self) -> "PageSeedSchema":
        """Ensure path starts with /."""
        if self.path != "/" and not self.path.startswith("/"):
            raise ValueError(f"Path must start with '/', got: {self.path}")
        return self

    @model_validator(mode="before")
    @classmethod
    def convert_metadata(cls, data: dict[str, Any]) -> dict[str, Any]:
        """Convert metadata dict to PageMetadataSchema if needed."""
        if "metadata" in data and isinstance(data["metadata"], dict):
            data["metadata"] = PageMetadataSchema(**data["metadata"])
        return data

    @classmethod
    def get_sst_path(cls) -> Path:
        """Get path to SST YAML file (from __index.yaml registry)."""
        return get_sst_path(cls.SST_KEY)

    @classmethod
    def load_sst_data(cls) -> dict[str, Any]:
        """Load SST YAML file."""
        sst_path = cls.get_sst_path()
        if not sst_path.exists():
            raise FileNotFoundError(f"SST file not found: {sst_path}")
        with open(sst_path) as f:
            return yaml.safe_load(f)

    @classmethod
    def get_defaults(cls) -> dict[str, Any]:
        """Get default values for page seeds from SST."""
        data = cls.load_sst_data()
        return data.get("defaults", {})

    @classmethod
    def _merge_defaults(cls, page_data: dict[str, Any], defaults: dict[str, Any]) -> dict[str, Any]:
        """Merge page data with defaults (page data takes precedence)."""
        merged = defaults.copy()
        merged.update(page_data)
        return merged

    @classmethod
    def get_all_for_site(cls, domain: str) -> list["PageSeedSchema"]:
        """
        Get all page seeds for a site from SST.

        Handles both single-site tenants and multi-site tenants.

        Args:
            domain: Site domain (e.g., "cineos.io", "store.joelschaeffer.com")

        Returns:
            List of PageSeedSchema instances

        Raises:
            KeyError: If site not found
        """
        data = cls.load_sst_data()
        defaults = data.get("defaults", {})
        tenants = data.get("tenants", {})

        # Search through tenants for the domain
        for _tenant_slug, tenant_data in tenants.items():
            # Check if this tenant has a direct 'site' field matching the domain
            if tenant_data.get("site") == domain:
                pages = tenant_data.get("pages", [])
                return [cls(**cls._merge_defaults(page, defaults)) for page in pages]

            # Check if tenant has multiple sites
            if "sites" in tenant_data:
                sites = tenant_data["sites"]
                if domain in sites:
                    pages = sites[domain].get("pages", [])
                    return [cls(**cls._merge_defaults(page, defaults)) for page in pages]

        # Domain not found
        all_domains = cls.get_all_sites()
        raise KeyError(f"Site '{domain}' not found in SST. Available: {all_domains}")

    @classmethod
    def get_all_sites(cls) -> list[str]:
        """Get list of all site domains with pages defined."""
        data = cls.load_sst_data()
        tenants = data.get("tenants", {})
        domains = []

        for tenant_data in tenants.values():
            # Single site tenant
            if "site" in tenant_data:
                domains.append(tenant_data["site"])
            # Multi-site tenant
            if "sites" in tenant_data:
                domains.extend(tenant_data["sites"].keys())

        return domains

    @classmethod
    def get_by_path(cls, domain: str, path: str) -> "PageSeedSchema":
        """
        Get a specific page seed by domain and path from SST.

        Args:
            domain: Site domain (e.g., "cineos.io")
            path: Page path (e.g., "/pricing/")

        Returns:
            PageSeedSchema instance

        Raises:
            KeyError: If page not found
        """
        pages = cls.get_all_for_site(domain)

        for page in pages:
            if page.path == path:
                return page

        available_paths = [p.path for p in pages]
        raise KeyError(f"Page '{path}' not found for site '{domain}'. Available: {available_paths}")

    @classmethod
    def get_tenant_for_domain(cls, domain: str) -> str:
        """
        Get the tenant slug that owns a domain.

        Args:
            domain: Site domain

        Returns:
            Tenant slug

        Raises:
            KeyError: If domain not found
        """
        data = cls.load_sst_data()
        tenants = data.get("tenants", {})

        for tenant_slug, tenant_data in tenants.items():
            if tenant_data.get("site") == domain:
                return tenant_slug
            if "sites" in tenant_data and domain in tenant_data["sites"]:
                return tenant_slug

        raise KeyError(f"No tenant found for domain '{domain}'")

    @classmethod
    def validate_against_sst(
        cls,
        domain: str,
        path: str,
        db_data: dict[str, Any],
    ) -> list[str]:
        """
        Validate database record against SST definition.

        Args:
            domain: Site domain
            path: Page path
            db_data: Dictionary of database record values

        Returns:
            List of drift errors (empty if valid)
        """
        errors = []

        try:
            sst_page = cls.get_by_path(domain, path)
        except KeyError as e:
            return [str(e)]

        # Check layout
        if db_data.get("layout") != sst_page.layout:
            errors.append(f"layout drift: DB='{db_data.get('layout')}' vs SST='{sst_page.layout}'")

        # Check blueprint (page_schema)
        db_blueprint = db_data.get("blueprint") or db_data.get("page_schema")
        if db_blueprint != sst_page.blueprint:
            errors.append(f"blueprint drift: DB='{db_blueprint}' vs SST='{sst_page.blueprint}'")

        # Check status
        if db_data.get("status") != sst_page.status:
            errors.append(f"status drift: DB='{db_data.get('status')}' vs SST='{sst_page.status}'")

        return errors
