from collections.abc import Generator
from unittest.mock import patch

import pytest
from fastapi.testclient import TestClient

from pygitweb.config import settings
from pygitweb.main import app


@pytest.fixture
def client() -> Generator[TestClient, None, None]:
	with TestClient(app) as c:
		yield c


def test_token_disabled(client: TestClient) -> None:
	with patch.object(settings, "AUTH", False):
		r = client.post("/token", data={"username": "a", "password": "b"})
		assert r.status_code == 400
		assert r.json()["detail"] == "Authentication is disabled"


def test_token_success(client: TestClient) -> None:
	with (
		patch.object(settings, "AUTH", True),
		patch.object(settings, "ADMIN_USER", "admin"),
		patch.object(settings, "ADMIN_PASSWORD", "secret"),
	):
		r = client.post("/token", data={"username": "admin", "password": "secret"})
	assert r.status_code == 200
	data = r.json()
	assert data["token_type"] == "bearer"
	assert isinstance(data["access_token"], str)
	assert len(data["access_token"]) >= 32
	assert data["access_token"] != "admin"


def test_token_wrong_password(client: TestClient) -> None:
	with (
		patch.object(settings, "AUTH", True),
		patch.object(settings, "ADMIN_USER", "admin"),
		patch.object(settings, "ADMIN_PASSWORD", "secret"),
	):
		r = client.post("/token", data={"username": "admin", "password": "wrong"})
	assert r.status_code == 400


def test_users_me_with_bearer(client: TestClient) -> None:
	with (
		patch.object(settings, "AUTH", True),
		patch.object(settings, "ADMIN_USER", "admin"),
		patch.object(settings, "ADMIN_PASSWORD", "secret"),
	):
		tok = client.post("/token", data={"username": "admin", "password": "secret"}).json()["access_token"]
		r = client.get("/users/me", headers={"Authorization": f"Bearer {tok}"})
		assert r.status_code == 200
		assert r.json()["username"] == "admin"


def test_login_form_sets_cookie_and_users_me(client: TestClient) -> None:
	with (
		patch.object(settings, "AUTH", True),
		patch.object(settings, "ADMIN_USER", "admin"),
		patch.object(settings, "ADMIN_PASSWORD", "secret"),
	):
		r = client.post(
			"/login",
			data={"username": "admin", "password": "secret", "next": "/"},
			follow_redirects=False,
		)
		assert r.status_code == 303
		r2 = client.get("/users/me")
		assert r2.status_code == 200
		assert r2.json()["username"] == "admin"


def test_logout_revokes_cookie_session(client: TestClient) -> None:
	with (
		patch.object(settings, "AUTH", True),
		patch.object(settings, "ADMIN_USER", "admin"),
		patch.object(settings, "ADMIN_PASSWORD", "secret"),
	):
		client.post(
			"/login",
			data={"username": "admin", "password": "secret", "next": "/"},
			follow_redirects=False,
		)
		assert client.get("/users/me").status_code == 200
		r_out = client.get("/logout", params={"next": "/"}, follow_redirects=False)
		assert r_out.status_code == 303
		assert client.get("/users/me").status_code == 401


def test_logout_with_bearer_revokes_token(client: TestClient) -> None:
	with (
		patch.object(settings, "AUTH", True),
		patch.object(settings, "ADMIN_USER", "admin"),
		patch.object(settings, "ADMIN_PASSWORD", "secret"),
	):
		tok = client.post("/token", data={"username": "admin", "password": "secret"}).json()["access_token"]
		headers = {"Authorization": f"Bearer {tok}"}
		assert client.get("/users/me", headers=headers).status_code == 200
		r_out = client.get("/logout", params={"next": "/"}, headers=headers, follow_redirects=False)
		assert r_out.status_code == 303
		assert client.get("/users/me", headers=headers).status_code == 401


def test_projectnamevalid_no_auth_required_when_auth_disabled(client: TestClient) -> None:
	with patch.object(settings, "AUTH", False), patch("pygitweb.main.project_visible_in_list", return_value=False):
		r = client.get("/projectnamevalid", params={"name": "newproj"})
	assert r.status_code == 200


def test_projectnamevalid_401_without_credentials_when_auth_enabled(client: TestClient) -> None:
	with (
		patch.object(settings, "AUTH", True),
		patch.object(settings, "ADMIN_USER", "admin"),
		patch.object(settings, "ADMIN_PASSWORD", "secret"),
	):
		r = client.get("/projectnamevalid", params={"name": "newproj"})
	assert r.status_code == 401


def test_projectnamevalid_ok_with_bearer_when_auth_enabled(client: TestClient) -> None:
	with (
		patch.object(settings, "AUTH", True),
		patch.object(settings, "ADMIN_USER", "admin"),
		patch.object(settings, "ADMIN_PASSWORD", "secret"),
		patch("pygitweb.main.project_visible_in_list", return_value=False),
	):
		tok = client.post("/token", data={"username": "admin", "password": "secret"}).json()["access_token"]
		r = client.get(
			"/projectnamevalid",
			params={"name": "newproj"},
			headers={"Authorization": f"Bearer {tok}"},
		)
	assert r.status_code == 200