from __future__ import annotations

import logging
import os
from typing import TYPE_CHECKING, Any, Optional

from staticsite import fields
from staticsite.feature import Feature
from staticsite.page import ChangeExtent, Page, StandaloneAutoPage, TemplatePage
from staticsite.source_node import SourcePageNode

if TYPE_CHECKING:
    from .. import file
    from ..node import Node
    from ..site import Site

log = logging.getLogger("dirindex")


class ParentField(fields.Field["Dir", Optional[Page]]):
    """
    Field that works as a proxy for page.node.parent.page
    """

    def __get__(self, page: Page, type: type[Dir] | None = None) -> Page | None:
        if (parent := page.node.parent) is None:
            return None
        return parent.page


class Dir(TemplatePage, StandaloneAutoPage):
    """
    Page with a directory index.

    For each directory that does not have an index page, staticsite automatically
    generates one: this makes every partial url to the site a valid URL.

    Directory pages have these extra properties:

    * `page.meta.template` defaults to `dir.html`
    * `page.meta.title` is the directory name, or the site name in case the site
      root is an autogenerated directory page
    * `page.meta.pages` lists the pages in this directory
    * `page.meta.parent` parent page in the directory hierarchy
    """

    TYPE = "dir"
    TEMPLATE = "dir.html"

    parent = ParentField(doc="Page one level above in the site hierarchy")

    def __init__(self, site: Site, *, name: str | None = None, **kw: Any):
        super().__init__(site, **kw)
        # Directory name
        self.name: str | None = name
        # Subdirectory of this directory
        self.subdirs: list[Page] = []

        # Pointer to the directory in the file system
        self.src: file.File

        self.syndicated = False
        self.indexed = False

        if self.node.parent:
            self.title = self.name

        pages: list[Page] = []
        for name, sub in self.node.sub.items():
            if sub.page:
                if sub.page.directory_index:
                    self.subdirs.append(sub.page)
                else:
                    pages.append(sub.page)
        for name, page in self.node.build_pages.items():
            if page != self and page.leaf and page.indexed:
                pages.append(page)
        pages.sort(key=lambda p: p.date)
        self.pages = pages

        # self.indexed = bool(self.pages) or any(p.indexed for p in self.subdirs)

        # Since directory indices is called from the bottom up, subdirs have
        # their date up to date
        self.subdirs.sort(key=lambda p: p.date)

    def crossreference(self) -> None:
        date_pages = []
        if self.subdirs:
            date_pages.append(self.subdirs[-1].date)
        if self.pages:
            date_pages.append(self.pages[-1].date)

        if date_pages:
            self.date = max(date_pages)
        else:
            self.date = self.site.localized_timestamp(self.src.stat.st_mtime)

    def _compute_change_extent(self) -> ChangeExtent:
        res = super()._compute_change_extent()

        # Check if pages were deleted in this dir
        for relpath in self.site.deleted_source_pages():
            if os.path.dirname(relpath) == self.src.relpath:
                return ChangeExtent.ALL

        # Dir has changed if any page referenced changed in metadata
        for subdir in self.subdirs:
            if subdir.change_extent == ChangeExtent.ALL:
                res = ChangeExtent.ALL
        if self.pages:
            for page in self.pages:
                if page.change_extent == ChangeExtent.ALL:
                    res = ChangeExtent.ALL
        return res


class DirindexFeature(Feature):
    """
    Build redirection pages for page aliases.

    A page can define 'aliases=[...]' to generate pages in those locations that
    redirect to the page.
    """

    def __init__(self, *args: Any, **kw: Any):
        super().__init__(*args, **kw)
        self.dir_pages: list[Dir] = []

    def get_used_page_types(self) -> list[type[Page]]:
        return [Dir]

    def scan(self, node: Node) -> None:
        for subnode in node.sub.values():
            self.scan(subnode)

        if isinstance(node, SourcePageNode) and not node.page:
            page = node.create_auto_page_as_index(page_cls=Dir, name=node.name)
            if page is not None:
                page.src = node.src
                self.dir_pages.append(page)

    def generate(self) -> None:
        # Scan the node hierarchy creating indices for nodes without an index
        # page
        self.scan(self.site.root)

    def crossreference(self) -> None:
        for page in self.dir_pages:
            page.crossreference()


FEATURES = {
    "dirindex": DirindexFeature,
}
