import json
from datetime import datetime, timedelta

import pytest
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine

import app.models  # noqa: F401
from app.database import Base
from app.main import app
from app.models.log import Log
from app.models.user import User
from app.routers.auth import get_current_user
from app.database import get_db
from app.services.auth_service import get_password_hash


@pytest.fixture
async def log_test_env(tmp_path):
    db_path = tmp_path / 'test_logs.db'
    engine = create_async_engine(f"sqlite+aiosqlite:///{db_path}", future=True)
    session_factory = async_sessionmaker(engine, expire_on_commit=False)

    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    async with session_factory() as session:
        current_user = User(
            email='tester@example.com',
            hashed_password=get_password_hash('secret123'),
            full_name='Tester',
        )
        other_user = User(
            email='other@example.com',
            hashed_password=get_password_hash('secret123'),
            full_name='Other',
        )
        session.add_all([current_user, other_user])
        await session.flush()

        now = datetime.utcnow()
        session.add_all(
            [
                Log(
                    level='error',
                    type='system',
                    user_id=current_user.id,
                    request_id='req-target',
                    route='/api/settings',
                    method='PUT',
                    status_code=500,
                    operation='settings.save',
                    message='target error',
                    source='settings',
                    details=json.dumps({'scope': 'target'}),
                    created_at=now - timedelta(minutes=30),
                    updated_at=now - timedelta(minutes=30),
                ),
                Log(
                    level='info',
                    type='system',
                    user_id=None,
                    request_id='req-global',
                    route='/api/health',
                    method='GET',
                    status_code=200,
                    operation='health.check',
                    message='global info',
                    source='http',
                    details=json.dumps({'scope': 'global'}),
                    created_at=now - timedelta(minutes=20),
                    updated_at=now - timedelta(minutes=20),
                ),
                Log(
                    level='error',
                    type='system',
                    user_id=other_user.id,
                    request_id='req-other',
                    route='/api/secret',
                    method='GET',
                    status_code=500,
                    operation='secret.fail',
                    message='other user error',
                    source='secret',
                    details=json.dumps({'scope': 'other'}),
                    created_at=now - timedelta(minutes=10),
                    updated_at=now - timedelta(minutes=10),
                ),
                Log(
                    level='warning',
                    type='chat',
                    user_id=current_user.id,
                    request_id='req-old',
                    route='/api/chat',
                    method='POST',
                    status_code=429,
                    operation='chat.send',
                    message='old warning',
                    source='chat',
                    details=json.dumps({'scope': 'old'}),
                    created_at=now - timedelta(days=10),
                    updated_at=now - timedelta(days=10),
                ),
            ]
        )
        await session.commit()
        await session.refresh(current_user)

    async def override_get_db():
        async with session_factory() as session:
            yield session

    async def override_get_current_user():
        return current_user

    app.dependency_overrides[get_db] = override_get_db
    app.dependency_overrides[get_current_user] = override_get_current_user

    try:
        yield
    finally:
        app.dependency_overrides.clear()
        await engine.dispose()


@pytest.mark.asyncio
async def test_logs_list_filters_by_route_and_hides_other_user_records(log_test_env):
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url='http://testserver') as client:
        response = await client.get('/api/logs', params={'route': '/api/settings'})

    assert response.status_code == 200
    payload = response.json()
    assert payload['total'] == 1
    assert [log['request_id'] for log in payload['logs']] == ['req-target']


@pytest.mark.asyncio
async def test_logs_stats_uses_same_filters_as_list(log_test_env):
    transport = ASGITransport(app=app)
    params = {
        'route': '/api/settings',
        'status_code': 500,
        'level': 'error',
    }

    async with AsyncClient(transport=transport, base_url='http://testserver') as client:
        list_response = await client.get('/api/logs', params=params)
        stats_response = await client.get('/api/logs/stats', params=params)

    assert list_response.status_code == 200
    assert stats_response.status_code == 200

    list_payload = list_response.json()
    stats_payload = stats_response.json()

    assert list_payload['total'] == 1
    assert stats_payload['total'] == 1
    assert stats_payload['by_type']['system'] == 1
    assert stats_payload['by_level']['error'] == 1


@pytest.mark.asyncio
async def test_logs_rejects_invalid_datetime_range(log_test_env):
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url='http://testserver') as client:
        response = await client.get(
            '/api/logs',
            params={
                'start_at': '2026-03-21T12:00:00+00:00',
                'end_at': '2026-03-20T12:00:00+00:00',
            },
        )

    assert response.status_code == 422
