from .typehints import LCHttpRequest, StrOrPromise, View

INTERESTING_URLS = ('_detail', '_list', '_landing', '_dashboard')
BREADCRUMB_TRAIL = 'breadcrumb_trail'
LEAF_CRUMB = 'leaf_crumb'
LATEST_TRAIL = 'latest_trail'
ROOT_CRUMB = 'root_crumb'


# ----------------------------------------------------------------------
class BreadcrumbTrail:
    """
    Manage page history for bread crumb generation
    """

    url_name: str

    def __init__(self, request: LCHttpRequest, name: StrOrPromise = '', icon: str = '') -> None:
        self.request = request
        self.session = request.session
        self.url_name = str(request.resolver_match.url_name if request.resolver_match else '')
        self.url_path = request.path  # don't want query params!

        self.name = name
        self.icon = icon

        if ROOT_CRUMB not in self.session:
            self.session[ROOT_CRUMB] = ()

        if BREADCRUMB_TRAIL not in self.session:
            self.session.update({
                BREADCRUMB_TRAIL: [],
                LATEST_TRAIL: [],
            })

        if LEAF_CRUMB not in self.session:
            self.session[LEAF_CRUMB] = ()

    def reset(self) -> None:
        """
        Clear trail of all prior steps. Useful when user applies Company Context Filter,
        and history might include pages not visible to the new company context.
        """
        self.session.update({
            BREADCRUMB_TRAIL: [],
            LATEST_TRAIL: [],
        })
        self.session[LEAF_CRUMB] = []

    def interesting_page_view(self) -> bool:
        """
        Was this request interesting enough to save in breadcrumb history?
        """
        if self.request.is_unpoly():
            return self.url_name.endswith(INTERESTING_URLS)
        return self.request.method == 'GET'

    def track_in_trail(self) -> bool:
        """
        update / create URLs are shown on the Leaf, but don't get added to trail
        """
        try:
            return not self.session[LEAF_CRUMB].url_name.endswith((
                '_create',
                '_update',
                '_delete',
                '_search',
            ))
        except AttributeError:
            return False

    def step_forward(self, step: View) -> None:
        """
        Some page views should only be visible on the Leaf,
        but not be added to the history trail.
        """
        if not self.session[LEAF_CRUMB]:
            self.session[LEAF_CRUMB] = step
            return

        if self.track_in_trail():
            self.session[BREADCRUMB_TRAIL].append(self.session[LEAF_CRUMB])

        self.session[LEAF_CRUMB] = step

    def set_root(self, data: dict) -> None:
        """
        Set the leftmost breadcrumb, usually the App Dashboard or Home
        """
        self.session[ROOT_CRUMB] = data

    def check_back_trail(self, data: View) -> int:
        """
        Was the user on this page before? Is he clicking prior links on the trail?
        If so, then jump back, rather than continuing the circle
        """
        try:
            return self.session[BREADCRUMB_TRAIL].index(data)
        except (AttributeError, ValueError):
            return -1

    def same_as_leaf(self) -> bool:
        """
        Was the data from this page view the same as the most recent page view?
        """
        crumb = self.session[LEAF_CRUMB]
        try:
            return crumb.url_path == self.url_path
        except AttributeError:
            return False

    def jump_back_trail(self, location: int) -> None:
        """
        Jump back to prior location in trail and set that position as leaf
        """
        self.session[LEAF_CRUMB] = self.session[BREADCRUMB_TRAIL][location]
        self.session[BREADCRUMB_TRAIL] = self.session[BREADCRUMB_TRAIL][:location]

    def trim_trail_length(self) -> None:
        """
        Chop the oldest crumb if trail is too long,
        unless this page visit won't get added to the trail
        """
        if not self.track_in_trail():
            return

        if len(self.session[BREADCRUMB_TRAIL]) > 10:
            self.session[BREADCRUMB_TRAIL].pop(0)

    def trail(self) -> list:
        """
        Update breadcrumb trail with this page view...

          * if this page is the same as the latest crumb, skip
          * if this page was visited earlier, remove all later crumbs
        """
        if not self.interesting_page_view() or self.same_as_leaf():
            return self.session[BREADCRUMB_TRAIL]

        try:
            data = View(
                name=str(self.name),  # cast to string so translated objects are JSON serializable
                icon=self.icon,
                url=self.url_path,
                url_name=self.url_name,
            )
        except AttributeError:
            return self.session[LATEST_TRAIL]

        visited_before = self.check_back_trail(data)
        if visited_before > -1:
            self.jump_back_trail(location=visited_before)
        else:
            self.step_forward(step=data)
            self.trim_trail_length()

        try:
            self.session[LATEST_TRAIL] = self.session[BREADCRUMB_TRAIL][-2:]
        except IndexError:
            self.session[LATEST_TRAIL] = self.session[BREADCRUMB_TRAIL]

        self.session.modified = True
        return self.session[LATEST_TRAIL]
