"""Shared test fixtures."""

import uuid
from unittest.mock import AsyncMock, MagicMock, patch

import pandas as pd
import pytest


@pytest.fixture(autouse=True)
def _bypass_metering(request):
    """Bypass MCP metering gate in all tests unless marked @pytest.mark.real_metering."""
    if "real_metering" in {m.name for m in request.node.iter_markers()}:
        yield
    else:
        with patch("mixlift_mcp.metering.check_and_increment", return_value=(True, "Run 1 of 2 this month")):
            yield
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession

from mixlift.auth.service import create_access_token
from mixlift.deps import get_current_user, get_db
from mixlift.main import app
from mixlift.projects.models import User


# --- Existing data fixtures ---


@pytest.fixture
def sample_meta_csv():
    """Sample Meta Ads CSV data."""
    return pd.DataFrame({
        "Date": pd.date_range("2023-01-01", periods=200, freq="D"),
        "Campaign Name": ["Prospecting"] * 100 + ["Retargeting"] * 100,
        "Amount Spent (USD)": [150.0 + i * 0.5 for i in range(200)],
        "Impressions": [5000 + i * 10 for i in range(200)],
        "Link Clicks": [200 + i for i in range(200)],
        "Purchases": [10 + i // 10 for i in range(200)],
    })


@pytest.fixture
def sample_google_csv():
    """Sample Google Ads CSV data."""
    return pd.DataFrame({
        "Date": pd.date_range("2023-01-01", periods=200, freq="D"),
        "Campaign": ["Search"] * 100 + ["Shopping"] * 100,
        "Cost": [120.0 + i * 0.3 for i in range(200)],
        "Impressions": [8000 + i * 20 for i in range(200)],
        "Clicks": [300 + i * 2 for i in range(200)],
        "Conversions": [15 + i // 8 for i in range(200)],
    })


@pytest.fixture
def sample_canonical_df():
    """Sample canonical format DataFrame (long format, multiple channels, ~30 weeks)."""
    dates = pd.date_range("2023-01-01", periods=210, freq="D")
    channels = ["Google Search", "Meta", "TikTok"]
    rows = []
    for date in dates:
        for channel in channels:
            rows.append({
                "date": date,
                "channel": channel,
                "spend": 100.0 + hash(f"{date}{channel}") % 200,
                "impressions": 5000 + hash(f"{date}{channel}i") % 3000,
                "clicks": 100 + hash(f"{date}{channel}c") % 200,
                "conversions": 5 + hash(f"{date}{channel}v") % 10,
            })
    return pd.DataFrame(rows)


@pytest.fixture
def synthetic_csv_path(tmp_path):
    """Generate a small synthetic CSV for testing."""
    from scripts.generate_synthetic import generate_synthetic_data

    output_path = str(tmp_path / "test_data.csv")
    generate_synthetic_data(n_weeks=30, seed=42, output_path=output_path)
    return output_path


# --- API test infrastructure ---


@pytest.fixture
def fake_user():
    return User(
        id=uuid.UUID("12345678-1234-5678-1234-567812345678"),
        email="test@example.com",
        password_hash="hashed",
        name="Test User",
        tier="free",
    )


@pytest.fixture
def fake_pro_user():
    return User(
        id=uuid.UUID("87654321-4321-8765-4321-876543218765"),
        email="pro@example.com",
        password_hash="hashed",
        name="Pro User",
        tier="pro",
    )


@pytest.fixture
def mock_session():
    session = AsyncMock(spec=AsyncSession)

    execute_result = AsyncMock()
    execute_result.scalar_one_or_none = MagicMock()
    execute_result.scalar_one = MagicMock()
    execute_result.scalar = MagicMock()

    scalars_mock = MagicMock()
    scalars_mock.all = MagicMock()
    execute_result.scalars = MagicMock(return_value=scalars_mock)

    session.execute = AsyncMock(return_value=execute_result)

    session.add = MagicMock()
    session.flush = AsyncMock()
    session.commit = AsyncMock()
    session.delete = AsyncMock()

    return session


@pytest.fixture
async def client(fake_user, mock_session):
    async def override_get_current_user():
        return fake_user

    async def override_get_db():
        yield mock_session

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

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac:
        yield ac

    app.dependency_overrides.clear()


@pytest.fixture
async def pro_client(fake_pro_user, mock_session):
    async def override_get_current_user():
        return fake_pro_user

    async def override_get_db():
        yield mock_session

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

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac:
        yield ac

    app.dependency_overrides.clear()


@pytest.fixture
def auth_headers(fake_user):
    token = create_access_token(str(fake_user.id))
    return {"Authorization": f"Bearer {token}"}
