from typing import Annotated

from django.http import HttpRequest, HttpResponse
from django.utils.translation import gettext as _
from pydantic import Json, StringConstraints

from zerver.actions.navigation_views import (
    do_add_navigation_view,
    do_remove_navigation_view,
    do_update_navigation_view,
)
from zerver.lib.exceptions import JsonableError
from zerver.lib.navigation_views import get_navigation_view, get_navigation_views_for_user
from zerver.lib.response import json_success
from zerver.lib.typed_endpoint import typed_endpoint
from zerver.models import NavigationView, UserProfile

BUILT_IN_VIEW_FRAGMENTS = [
    "inbox",
    "recent",
    "feed",
    "drafts",
    # Reactions view
    "narrow/has/reaction/sender/me",
    # Mentions view
    "narrow/is/mentioned",
    # Starred messages view
    "narrow/is/starred",
    "scheduled",
    "reminders",
]


def get_navigation_views(
    request: HttpRequest,
    user_profile: UserProfile,
) -> HttpResponse:
    """
    Fetch navigation views for the specified user.
    """
    navigation_views = get_navigation_views_for_user(user_profile)
    return json_success(request, data={"navigation_views": navigation_views})


@typed_endpoint
def add_navigation_view(
    request: HttpRequest,
    user_profile: UserProfile,
    *,
    fragment: Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)],
    is_pinned: Json[bool],
    name: Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)] | None = None,
) -> HttpResponse:
    """
    Add a new navigation view (or settings for a default view) for the user.
    """
    if fragment in BUILT_IN_VIEW_FRAGMENTS:
        if name is not None:
            raise JsonableError(_("Built-in views cannot have a custom name."))
    else:
        if not name:
            raise JsonableError(_("Custom views must have a valid name."))

    if NavigationView.objects.filter(user=user_profile, fragment=fragment).exists():
        raise JsonableError(_("Navigation view already exists."))
    if name is not None and NavigationView.objects.filter(user=user_profile, name=name).exists():
        raise JsonableError(_("Navigation view already exists."))

    do_add_navigation_view(
        user_profile,
        fragment,
        is_pinned,
        name,
    )
    return json_success(request)


@typed_endpoint
def update_navigation_view(
    request: HttpRequest,
    user_profile: UserProfile,
    fragment: Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)],
    *,
    is_pinned: Json[bool] | None = None,
    name: Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)] | None = None,
) -> HttpResponse:
    """
    Update an existing navigation view for the user.
    """
    if fragment in BUILT_IN_VIEW_FRAGMENTS and name is not None:
        raise JsonableError(_("Built-in views cannot have a custom name."))
    if name is not None and NavigationView.objects.filter(user=user_profile, name=name).exists():
        raise JsonableError(_("Navigation view already exists."))

    navigation_view = get_navigation_view(user_profile, fragment)

    do_update_navigation_view(
        user_profile,
        navigation_view,
        is_pinned,
        name,
    )
    return json_success(request)


def remove_navigation_view(
    request: HttpRequest,
    user_profile: UserProfile,
    *,
    fragment: Annotated[str, StringConstraints(strip_whitespace=True, min_length=1)],
) -> HttpResponse:
    """
    Remove a navigation view for the user.
    """
    navigation_view = get_navigation_view(user_profile, fragment)
    do_remove_navigation_view(user_profile, navigation_view)
    return json_success(request)
