"""
URL Strategy - Centralized URL and path computation.

Provides pure utility functions for computing output paths and URLs.
Used by orchestrators to ensure consistent path generation across the system.
"""


from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from bengal.core.page import Page
    from bengal.core.section import Section
    from bengal.core.site import Site


class URLStrategy:
    """
    Pure utility for URL and output path computation.

    Centralizes all path/URL logic to ensure consistency and prevent bugs.
    All methods are static - no state, pure logic.

    Design principles:
    - Pure functions (no side effects)
    - No dependencies on global state
    - Easy to test in isolation
    - Reusable across orchestrators
    """

    @staticmethod
    def compute_regular_page_output_path(
        page: Page, site: Site, pre_cascade: bool = False
    ) -> Path:
        """
        Compute output path for a regular content page.

        Args:
            page: Page object (must have source_path set)
            site: Site object (for output_dir and config)

        Returns:
            Absolute path where the page HTML should be written

        Examples:
            content/about.md → public/about/index.html (pretty URLs)
            content/blog/post.md → public/blog/post/index.html
            content/docs/_index.md → public/docs/index.html
        """
        content_dir = site.root_path / "content"
        pretty_urls = site.config.get("pretty_urls", True)
        # i18n configuration (optional)
        i18n = site.config.get("i18n", {}) or {}
        strategy = i18n.get("strategy", "none")
        default_lang = i18n.get("default_language", "en")
        default_in_subdir = bool(i18n.get("default_in_subdir", False))

        # Get relative path from content directory
        try:
            rel_path = page.source_path.relative_to(content_dir)
        except ValueError:
            # Not under content_dir (shouldn't happen for regular pages)
            rel_path = Path(page.source_path.name)

        if pre_cascade:
            # For pre-cascade, use source_path as-is without modifications
            rel_path = page.source_path.relative_to(content_dir)

        # Change extension to .html
        output_rel_path = rel_path.with_suffix(".html")

        # Apply URL rules
        if pretty_urls:
            if output_rel_path.stem in ("index", "_index"):
                # _index.md → index.html (keep in same directory)
                output_rel_path = output_rel_path.parent / "index.html"
            else:
                # about.md → about/index.html (directory structure)
                output_rel_path = output_rel_path.parent / output_rel_path.stem / "index.html"
        # Flat URLs: about.md → about.html
        elif output_rel_path.stem == "_index":
            output_rel_path = output_rel_path.parent / "index.html"

        # Apply i18n URL strategy (prefix)
        if strategy == "prefix":
            lang: str | None = getattr(page, "lang", None)
            # If default language should be under subdir or non-default language: prefix
            if lang and (default_in_subdir or lang != default_lang):
                output_rel_path = Path(lang) / output_rel_path
        # strategy 'domain' or 'none' → no path prefixing here
        return site.output_dir / output_rel_path

    @staticmethod
    def compute_archive_output_path(section: Section, page_num: int, site: Site) -> Path:
        """
        Compute output path for a section archive page.

        Args:
            section: Section to create archive for
            page_num: Page number (1 for first page, 2+ for pagination)
            site: Site object (for output_dir)

        Returns:
            Absolute path where the archive HTML should be written

        Examples:
            section='docs', page=1 → public/docs/index.html
            section='docs', page=2 → public/docs/page/2/index.html
            section='docs/markdown', page=1 → public/docs/markdown/index.html
        """
        # Get full hierarchy (excluding 'root')
        hierarchy = [h for h in section.hierarchy if h != "root"]

        # Build base path
        path = site.output_dir
        for segment in hierarchy:
            path = path / segment

        # Add pagination if needed
        if page_num > 1:
            path = path / "page" / str(page_num)

        return path / "index.html"

    @staticmethod
    def compute_tag_output_path(tag_slug: str, page_num: int, site: Site) -> Path:
        """
        Compute output path for a tag listing page.

        Args:
            tag_slug: URL-safe tag identifier
            page_num: Page number (1 for first page, 2+ for pagination)
            site: Site object (for output_dir)

        Returns:
            Absolute path where the tag page HTML should be written

        Examples:
            tag='python', page=1 → public/tags/python/index.html
            tag='python', page=2 → public/tags/python/page/2/index.html
        """
        # i18n prefix support using site's current language context
        i18n = site.config.get("i18n", {}) or {}
        strategy = i18n.get("strategy", "none")
        default_lang = i18n.get("default_language", "en")
        default_in_subdir = bool(i18n.get("default_in_subdir", False))
        lang = getattr(site, "current_language", None)

        base_path = site.output_dir
        if strategy == "prefix" and lang and (default_in_subdir or lang != default_lang):
            base_path = base_path / lang

        path = base_path / "tags" / tag_slug

        # Add pagination if needed
        if page_num > 1:
            path = path / "page" / str(page_num)

        return path / "index.html"

    @staticmethod
    def compute_tag_index_output_path(site: Site) -> Path:
        """
        Compute output path for the main tags index page.

        Args:
            site: Site object (for output_dir)

        Returns:
            Absolute path where the tags index HTML should be written

        Example:
            public/tags/index.html
        """
        # i18n prefix support using site's current language context
        i18n = site.config.get("i18n", {}) or {}
        strategy = i18n.get("strategy", "none")
        default_lang = i18n.get("default_language", "en")
        default_in_subdir = bool(i18n.get("default_in_subdir", False))
        lang = getattr(site, "current_language", None)

        base_path = site.output_dir
        if strategy == "prefix" and lang and (default_in_subdir or lang != default_lang):
            base_path = base_path / lang

        return base_path / "tags" / "index.html"

    @staticmethod
    def url_from_output_path(output_path: Path, site: Site) -> str:
        """
        Generate clean URL from output path.

        Args:
            output_path: Absolute path to output file
            site: Site object (for output_dir)

        Returns:
            Clean URL with leading/trailing slashes

        Examples:
            public/about/index.html → /about/
            public/docs/guide.html → /docs/guide/
            public/index.html → /

        Raises:
            ValueError: If output_path is not under site.output_dir
        """
        try:
            rel_path = output_path.relative_to(site.output_dir)
        except ValueError:
            raise ValueError(
                f"Output path {output_path} is not under output directory {site.output_dir}"
            ) from None

        # Convert to URL parts
        url_parts = list(rel_path.parts)

        # Remove index.html (implicit in URLs)
        if url_parts and url_parts[-1] == "index.html":
            url_parts = url_parts[:-1]
        elif url_parts and url_parts[-1].endswith(".html"):
            # Non-index: remove .html extension
            url_parts[-1] = url_parts[-1][:-5]

        # Build URL
        if not url_parts:
            return "/"

        url = "/" + "/".join(url_parts)

        # Ensure trailing slash
        if not url.endswith("/"):
            url += "/"

        return url

    @staticmethod
    def make_virtual_path(site: Site, *parts: str) -> Path:
        """
        Create virtual source path for generated pages.

        Generated pages (archives, tags, etc.) don't have real source files.
        This creates a virtual path under .bengal/generated/ for tracking.

        Args:
            site: Site object (for root_path)
            *parts: Path components

        Returns:
            Virtual path under .bengal/generated/

        Examples:
            make_virtual_path(site, 'archives', 'docs')
            → /path/to/site/.bengal/generated/archives/docs/index.md

            make_virtual_path(site, 'tags', 'python')
            → /path/to/site/.bengal/generated/tags/python/index.md
        """
        return site.root_path / ".bengal" / "generated" / Path(*parts) / "index.md"
