from __future__ import annotations

from collections.abc import Callable

import pytest
from fastapi.testclient import TestClient

from models.navigation import NavigationGroup, NavigationLink
from services.unit_of_work import UnitOfWork


@pytest.fixture()
def seed_calculation_navigation(
    unit_of_work_factory: Callable[[], UnitOfWork]
) -> Callable[[], None]:
    def _seed() -> None:
        with unit_of_work_factory() as uow:
            repo = uow.navigation
            assert repo is not None

            workspace = repo.add_group(
                NavigationGroup(
                    slug="workspace",
                    label="Workspace",
                    sort_order=10,
                )
            )

            projects_link = repo.add_link(
                NavigationLink(
                    group_id=workspace.id,
                    slug="projects",
                    label="Projects",
                    href_override="/projects",
                    sort_order=5,
                    required_roles=[],
                )
            )
            repo.add_link(
                NavigationLink(
                    group_id=workspace.id,
                    parent_link_id=projects_link.id,
                    slug="profitability",
                    label="Profitability Calculator",
                    route_name="calculations.profitability_form",
                    href_override="/calculations/profitability",
                    match_prefix="/calculations/profitability",
                    sort_order=8,
                    required_roles=["analyst", "admin"],
                )
            )
            repo.add_link(
                NavigationLink(
                    group_id=workspace.id,
                    parent_link_id=projects_link.id,
                    slug="opex",
                    label="Opex Planner",
                    route_name="calculations.opex_form",
                    href_override="/calculations/opex",
                    match_prefix="/calculations/opex",
                    sort_order=10,
                    required_roles=["analyst", "admin"],
                )
            )
            repo.add_link(
                NavigationLink(
                    group_id=workspace.id,
                    parent_link_id=projects_link.id,
                    slug="capex",
                    label="Capex Planner",
                    route_name="calculations.capex_form",
                    href_override="/calculations/capex",
                    match_prefix="/calculations/capex",
                    sort_order=15,
                    required_roles=["analyst", "admin"],
                )
            )

    return _seed


def test_navigation_sidebar_includes_calculation_links_for_admin(
    client: TestClient,
    seed_calculation_navigation: Callable[[], None],
) -> None:
    seed_calculation_navigation()

    response = client.get("/navigation/sidebar")
    assert response.status_code == 200
    payload = response.json()

    groups = payload["groups"]
    assert groups
    workspace = next(
        group for group in groups if group["label"] == "Workspace")
    workspace_links = workspace["links"]
    assert [link["label"] for link in workspace_links] == ["Projects"]

    projects_children = workspace_links[0]["children"]
    child_labels = [link["label"] for link in projects_children]
    assert child_labels == [
        "Profitability Calculator",
        "Opex Planner",
        "Capex Planner",
    ]

    profitability_link = next(
        link for link in projects_children if link["label"] == "Profitability Calculator")
    assert profitability_link["href"] == "/calculations/profitability"
    assert profitability_link["match_prefix"] == "/calculations/profitability"

    opex_link = next(
        link for link in projects_children if link["label"] == "Opex Planner")
    assert opex_link["href"] == "/calculations/opex"
    assert opex_link["match_prefix"] == "/calculations/opex"

    capex_link = next(
        link for link in projects_children if link["label"] == "Capex Planner")
    assert capex_link["href"] == "/calculations/capex"
    assert capex_link["match_prefix"] == "/calculations/capex"
    assert payload["roles"] == ["admin"]


def test_navigation_sidebar_hides_calculation_links_for_viewer_without_role(
    client: TestClient,
    seed_calculation_navigation: Callable[[], None],
    test_user_headers: Callable[[str | None], dict[str, str]],
) -> None:
    seed_calculation_navigation()

    response = client.get(
        "/navigation/sidebar",
        headers=test_user_headers("viewer"),
    )
    assert response.status_code == 200
    payload = response.json()

    groups = payload["groups"]
    assert groups
    workspace = next(
        group for group in groups if group["label"] == "Workspace")
    workspace_links = workspace["links"]
    assert [link["label"] for link in workspace_links] == ["Projects"]
    assert workspace_links[0]["children"] == []
    assert payload["roles"] == ["viewer"]


def test_navigation_sidebar_includes_contextual_urls_when_ids_provided(
    client: TestClient,
    seed_calculation_navigation: Callable[[], None],
) -> None:
    seed_calculation_navigation()

    response = client.get(
        "/navigation/sidebar",
        params={"project_id": "5", "scenario_id": "11"},
    )
    assert response.status_code == 200
    payload = response.json()

    workspace = next(
        group for group in payload["groups"] if group["label"] == "Workspace")
    projects = workspace["links"][0]
    capex_link = next(
        link for link in projects["children"] if link["label"] == "Capex Planner")

    assert capex_link["href"].endswith(
        "/calculations/projects/5/scenarios/11/calculations/capex"
    )
    assert capex_link["match_prefix"] == "/calculations/capex"
