import pytest
import pytest_asyncio
import asyncio
from fastapi import status
from fastapi.testclient import TestClient
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

from src.customer.models import Customer
from src.database import Base, get_db
from src.main import app
from src.user.models import User

DATABASE_URL = "sqlite+aiosqlite:///:memory:"

# --- Налаштування БД і сесії ---

engine = create_async_engine(DATABASE_URL, future=True, echo=False)
AsyncSessionLocal = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)

@pytest_asyncio.fixture(scope="session")
async def connection():
    async with engine.connect() as conn:
        await conn.run_sync(Base.metadata.create_all)
        yield conn
        await conn.run_sync(Base.metadata.drop_all)

@pytest_asyncio.fixture()
async def db_session(connection):
    async with connection.begin_nested():
        async with AsyncSession(bind=connection, expire_on_commit=False) as session:
            yield session
        # rollback автоматичний

@pytest_asyncio.fixture()
async def override_get_db(db_session):
    async def _override():
        yield db_session
    app.dependency_overrides[get_db] = _override
    yield
    app.dependency_overrides.clear()

# Синхронна фікстура для TestClient
@pytest.fixture()
def client(override_get_db):
    with TestClient(app) as c:
        yield c

# --- Фікстури створення користувачів ---

VALID_HASHED_PASSWORD = "$2b$12$KIX/fQY/pHOn8e.CCGXR7.5xRb4dQhLMucJXkJX.GCMYbsc.JVtuO"

@pytest_asyncio.fixture()
async def create_customer(db_session):
    async def _create_customer(phone="testphone", password=VALID_HASHED_PASSWORD, name="Test Customer"):
        customer = Customer(phone=phone, password=password, name=name)
        db_session.add(customer)
        await db_session.flush()
        await db_session.refresh(customer)
        return customer
    return _create_customer

@pytest_asyncio.fixture()
async def create_user(db_session):
    async def _create_user(
        login="testuser",
        password=VALID_HASHED_PASSWORD,
        name="Test User",
        role_id=1,
        phone="1234567890",
        logo=""  # дефолтне значення
    ):
        user = User(login=login, password=password, name=name, role_id=role_id, phone=phone, logo=logo)
        db_session.add(user)
        await db_session.flush()
        await db_session.refresh(user)
        return user
    return _create_user

# --- Тести ---

@pytest.mark.asyncio
async def test_login_success_customer(create_customer, db_session):
    customer = await create_customer()
    from unittest.mock import patch
    from src.auth.schemas import LoginRequest
    from src.auth.service import AuthService

    with patch("src.auth.service.verify_password", return_value=True), \
         patch("src.auth.service.create_access_token", return_value="testtoken"):

        data = LoginRequest(login=customer.phone, password="any_password")
        result = await AuthService.login(data, db_session, "127.0.0.1")

        assert result["access_token"] == "testtoken"
        assert result["account_type"] == "customer"
        assert result["account_data"]["login"] == customer.phone
        assert result["account_data"]["name"] == customer.name

@pytest.mark.asyncio
async def test_login_success_user(create_user, db_session):
    user = await create_user()
    from unittest.mock import patch
    from src.auth.schemas import LoginRequest
    from src.auth.service import AuthService

    with patch("src.auth.service.verify_password", return_value=True), \
         patch("src.auth.service.create_access_token", return_value="usertoken"):

        data = LoginRequest(login=user.login, password="any_password")
        result = await AuthService.login(data, db_session, "127.0.0.1")

        assert result["access_token"] == "usertoken"
        assert result["account_type"] == "user"
        assert result["account_data"]["login"] == user.login
        assert result["account_data"]["name"] == user.name

@pytest.mark.asyncio
async def test_login_invalid_credentials(db_session):
    from src.auth.schemas import LoginRequest
    from src.auth.service import AuthService
    from fastapi import HTTPException
    from unittest.mock import patch

    data = LoginRequest(login="notexist", password="pwd")

    with patch("src.auth.service.verify_password", return_value=False):
        with pytest.raises(HTTPException) as excinfo:
            await AuthService.login(data, db_session, "127.0.0.1")
        assert excinfo.value.status_code == status.HTTP_401_UNAUTHORIZED
        assert "Invalid credentials" in excinfo.value.detail

def test_login_api(client, create_customer):
    # унікальний телефон, щоб не було конфлікту
    unique_phone = "testphone_api"
    customer = asyncio.run(create_customer(phone=unique_phone))

    from unittest.mock import patch

    with patch("src.auth.service.verify_password", return_value=True), \
         patch("src.auth.service.create_access_token", return_value="apitoken"):

        response = client.post(
            "/login",
            json={"login": unique_phone, "password": "any"}
        )
        assert response.status_code == status.HTTP_200_OK
        json_data = response.json()
        assert "status" in json_data
        assert json_data["status"] is True
        assert "data" in json_data
        assert json_data["data"]["access_token"] == "apitoken"
        assert json_data["data"]["account_type"] == "customer"
        assert json_data["data"]["account_data"]["login"] == unique_phone
