"""Router dependency injection tests.

These tests verify that routers can consume service abstractions via FastAPI
dependencies and that those dependencies can be overridden cleanly for unit
testing without touching concrete implementations.
"""

from __future__ import annotations

from pathlib import Path

import aiosqlite
from httpx import ASGITransport, AsyncClient

from app.config import Settings
from app.db import init_db
from app.dependencies import get_auth_service, get_jail_service
from app.main import create_app
from app.models.auth import Session
from app.models.jail import JailListResponse
from app.utils.constants import SESSION_COOKIE_NAME
from app.utils.setup_state import set_setup_complete_cache


class FakeAuthService:
    async def login(
        self,
        _db: aiosqlite.Connection,
        password: str,
        session_duration_minutes: int,
        session_repo: object | None = None,
    ) -> Session:
        return Session(
            id=1,
            token="fake-token",
            created_at="2025-01-01T00:00:00Z",
            expires_at="2099-01-01T00:00:00Z",
        )

    async def validate_session(
        self,
        _db: aiosqlite.Connection,
        token: str,
        session_secret: str | None = None,
        session_repo: object | None = None,
    ) -> Session:
        return Session(
            id=1,
            token=token,
            created_at="2025-01-01T00:00:00Z",
            expires_at="2099-01-01T00:00:00Z",
        )

    async def logout(
        self,
        _db: aiosqlite.Connection,
        token: str,
        session_secret: str | None = None,
        session_repo: object | None = None,
    ) -> str | None:
        return token


class FakeJailService:
    async def list_jails(self, _socket_path: str) -> JailListResponse:
        return JailListResponse(jails=[], total=0)

    async def get_jail(self, _socket_path: str, _name: str) -> JailListResponse:
        raise NotImplementedError

    async def reload_all(self, _socket_path: str) -> None:
        raise NotImplementedError

    async def start_jail(self, _socket_path: str, _name: str) -> None:
        raise NotImplementedError

    async def stop_jail(self, _socket_path: str, _name: str) -> None:
        raise NotImplementedError

    async def set_idle(self, socket_path: str, name: str, *, on: bool) -> None:
        raise NotImplementedError

    async def reload_jail(self, socket_path: str, name: str) -> None:
        raise NotImplementedError

    async def get_ignore_list(self, socket_path: str, name: str) -> list[str]:
        raise NotImplementedError

    async def add_ignore_ip(self, socket_path: str, name: str, ip: str) -> None:
        raise NotImplementedError

    async def del_ignore_ip(self, socket_path: str, name: str, ip: str) -> None:
        raise NotImplementedError

    async def set_ignore_self(self, socket_path: str, name: str, *, on: bool) -> None:
        raise NotImplementedError

    async def get_jail_banned_ips(
        self,
        socket_path: str,
        jail_name: str,
        page: int,
        page_size: int,
        search: str | None = None,
        *,
        geo_batch_lookup: object,
        http_session: object,
        app_db: aiosqlite.Connection,
    ) -> JailListResponse:
        raise NotImplementedError


async def _build_app(settings: Settings):
    app = create_app(settings=settings)
    set_setup_complete_cache(app, True)
    db = await aiosqlite.connect(settings.database_path)
    db.row_factory = aiosqlite.Row
    await init_db(db)
    return app, db


async def test_auth_login_uses_injected_auth_service(tmp_path: Path) -> None:
    settings = Settings(
        database_path=str(tmp_path / "test_bangui.db"),
        fail2ban_socket="/tmp/fake_fail2ban.sock",
        fail2ban_config_dir=str(tmp_path / "fail2ban"),
        session_secret="test-secret-key-do-not-use-in-production",
        session_duration_minutes=60,
        timezone="UTC",
        log_level="debug",
    )

    app, db = await _build_app(settings)
    def _fake_auth_service() -> FakeAuthService:
        return FakeAuthService()

    app.dependency_overrides[get_auth_service] = _fake_auth_service

    transport = ASGITransport(app=app)
    async with AsyncClient(
        transport=transport,
        base_url="http://test",
    ) as client:
        response = await client.post(
            "/api/auth/login",
            json={"password": "ignored"},
        )

    await db.close()

    assert response.status_code == 200
    assert response.json()["token"].startswith("fake-token")
    assert response.cookies.get(SESSION_COOKIE_NAME) is not None


async def test_jail_list_uses_injected_jail_service_and_auth(tmp_path: Path) -> None:
    settings = Settings(
        database_path=str(tmp_path / "test_bangui.db"),
        fail2ban_socket="/tmp/fake_fail2ban.sock",
        fail2ban_config_dir=str(tmp_path / "fail2ban"),
        session_secret="test-secret-key-do-not-use-in-production",
        session_duration_minutes=60,
        timezone="UTC",
        log_level="debug",
    )

    app, db = await _build_app(settings)
    def _fake_auth_service() -> FakeAuthService:
        return FakeAuthService()

    def _fake_jail_service() -> FakeJailService:
        return FakeJailService()

    app.dependency_overrides[get_auth_service] = _fake_auth_service
    app.dependency_overrides[get_jail_service] = _fake_jail_service

    transport = ASGITransport(app=app)
    async with AsyncClient(
        transport=transport,
        base_url="http://test",
    ) as client:
        response = await client.get(
            "/api/jails",
            headers={"Cookie": f"{SESSION_COOKIE_NAME}=fake-token"},
        )

    await db.close()

    assert response.status_code == 200
    assert response.json() == {"jails": [], "total": 0}
