from __future__ import annotations

import unittest
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch

from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from httpx import ASGITransport, AsyncClient

from app.auth import get_current_user
from app.routes import admin, profile


class PasswordRoutesTest(unittest.IsolatedAsyncioTestCase):
    async def asyncSetUp(self) -> None:
        self.app = FastAPI()
        self.app.include_router(profile.router)
        self.app.include_router(admin.router)

        async def override_current_user():
            return SimpleNamespace(username="admin", role="admin")

        self.app.dependency_overrides[get_current_user] = override_current_user
        for route in admin.router.routes:
            for dependency in route.dependant.dependencies:
                if getattr(dependency.call, "__name__", "") == "role_checker":
                    self.app.dependency_overrides[dependency.call] = override_current_user
        self.transport = ASGITransport(app=self.app)

    async def asyncTearDown(self) -> None:
        self.app.dependency_overrides.clear()

    async def test_profile_page_renders_current_password_field(self) -> None:
        async with AsyncClient(transport=self.transport, base_url="http://testserver") as client:
            response = await client.get("/perfil")

        self.assertEqual(response.status_code, 200)
        self.assertIn('name="current_password"', response.text)
        self.assertIn('data-app-shell-sidebar', response.text)
        self.assertIn('/static/icons/settings-check.svg', response.text)
        self.assertIn('/static/icons/menu-2.svg', response.text)
        self.assertIn('/static/js/user-menu.js', response.text)
        self.assertIn('class="ui-header__leading"', response.text)
        self.assertIn("Panel admin", response.text)
        self.assertNotIn("Volver al dashboard", response.text)
        self.assertNotIn("Volver a casos", response.text)
        self.assertIn('data-user-menu-root', response.text)
        self.assertIn("Mi perfil", response.text)
        self.assertIn("Cerrar sesión", response.text)

    async def test_profile_password_change_success_renders_confirmation(self) -> None:
        with patch("app.routes.profile.change_own_password", new=AsyncMock()) as change_password:
            async with AsyncClient(transport=self.transport, base_url="http://testserver") as client:
                response = await client.post(
                    "/perfil/cambiar-password",
                    data={
                        "current_password": "actual123",
                        "new_password": "nueva123",
                        "confirm_password": "nueva123",
                    },
                )

        self.assertEqual(response.status_code, 200)
        self.assertIn("Contraseña actualizada correctamente.", response.text)
        change_password.assert_awaited_once_with("admin", "actual123", "nueva123")

    async def test_profile_password_change_renders_business_error(self) -> None:
        with patch(
            "app.routes.profile.change_own_password",
            new=AsyncMock(side_effect=profile.PasswordPolicyError("La contraseña actual no es válida.")),
        ):
            async with AsyncClient(transport=self.transport, base_url="http://testserver") as client:
                response = await client.post(
                    "/perfil/cambiar-password",
                    data={
                        "current_password": "incorrecta",
                        "new_password": "nueva123",
                        "confirm_password": "nueva123",
                    },
                )

        self.assertEqual(response.status_code, 200)
        self.assertIn("La contraseña actual no es válida.", response.text)

    async def test_admin_password_change_success_redirects(self) -> None:
        with patch("app.routes.admin.reset_user_password", new=AsyncMock()) as reset_password:
            async with AsyncClient(transport=self.transport, base_url="http://testserver") as client:
                response = await client.post(
                    "/admin/usuarios/cambiar-password",
                    data={"username": "medico1", "new_password": "nueva123"},
                    follow_redirects=False,
                )

        self.assertEqual(response.status_code, 303)
        self.assertIn("clave_exito=", response.headers["location"])
        reset_password.assert_awaited_once_with("admin", "medico1", "nueva123")

    async def test_admin_password_change_renders_user_not_found(self) -> None:
        with patch(
            "app.routes.admin.reset_user_password",
            new=AsyncMock(side_effect=admin.PasswordPolicyError("Usuario no encontrado.")),
        ), patch(
            "app.routes.admin._render_panel_admin",
            new=AsyncMock(return_value=HTMLResponse("Usuario no encontrado.", status_code=200)),
        ):
            async with AsyncClient(transport=self.transport, base_url="http://testserver") as client:
                response = await client.post(
                    "/admin/usuarios/cambiar-password",
                    data={"username": "medico1", "new_password": "nueva123"},
                )

        self.assertEqual(response.status_code, 200)
        self.assertIn("Usuario no encontrado.", response.text)


if __name__ == "__main__":
    unittest.main()
