"""Tests for auth service and auth API routes."""

import uuid
from unittest.mock import AsyncMock, patch

import pytest
from httpx import ASGITransport, AsyncClient

from mixlift.auth.service import (
    create_access_token,
    create_refresh_token,
    decode_token,
    hash_password,
    verify_password,
)
from mixlift.main import app


class TestPasswordHashing:
    def test_hash_and_verify(self):
        password = "testpassword123"
        hashed = hash_password(password)
        assert hashed != password
        assert verify_password(password, hashed)

    def test_wrong_password(self):
        hashed = hash_password("correct")
        assert not verify_password("wrong", hashed)


class TestJWT:
    def test_create_and_decode_access_token(self):
        user_id = "test-user-id"
        token = create_access_token(user_id)
        payload = decode_token(token)
        assert payload is not None
        assert payload["sub"] == user_id
        assert payload["type"] == "access"

    def test_create_and_decode_refresh_token(self):
        user_id = "test-user-id"
        token = create_refresh_token(user_id)
        payload = decode_token(token)
        assert payload is not None
        assert payload["sub"] == user_id
        assert payload["type"] == "refresh"

    def test_invalid_token(self):
        payload = decode_token("invalid-token")
        assert payload is None

    def test_different_tokens(self):
        access = create_access_token("user1")
        refresh = create_refresh_token("user1")
        assert access != refresh


class TestAuthRoutes:
    async def test_register_success(self, client, mock_session):
        mock_session.execute.return_value.scalar_one_or_none.return_value = None

        async def fake_flush(*args, **kwargs):
            user = mock_session.add.call_args[0][0]
            user.id = uuid.uuid4()

        mock_session.flush.side_effect = fake_flush

        response = await client.post(
            "/auth/register",
            json={"email": "new@example.com", "password": "securepass", "name": "New User"},
        )

        assert response.status_code == 201
        data = response.json()
        assert "access_token" in data
        assert "refresh_token" in data

    async def test_login_success(self, client, fake_user):
        with patch(
            "mixlift.auth.router.authenticate_user", new_callable=AsyncMock
        ) as mock_auth:
            mock_auth.return_value = fake_user
            response = await client.post(
                "/auth/login",
                json={"email": fake_user.email, "password": "testpassword"},
            )

        assert response.status_code == 200
        data = response.json()
        assert "access_token" in data
        assert "refresh_token" in data

    async def test_login_wrong_password(self, client):
        with patch(
            "mixlift.auth.router.authenticate_user", new_callable=AsyncMock
        ) as mock_auth:
            mock_auth.return_value = None
            response = await client.post(
                "/auth/login",
                json={"email": "test@example.com", "password": "wrong"},
            )

        assert response.status_code == 401

    async def test_me_authenticated(self, client, fake_user):
        response = await client.get("/auth/me")

        assert response.status_code == 200
        data = response.json()
        assert data["email"] == fake_user.email
        assert data["tier"] == fake_user.tier

    async def test_me_no_token(self):
        async with AsyncClient(
            transport=ASGITransport(app=app), base_url="http://test"
        ) as raw_client:
            response = await raw_client.get("/auth/me")

        assert response.status_code == 401
