"""Shared fixtures for the BulkGmailMailer test suite.

Strategy:
  - Unit tests (pure logic: variables, invoice numbers, CSV/TXT parsing,
    validation, error classification, rotation) run with no external services.
  - API integration tests run against a REAL PostgreSQL instance (the same
    engine the app uses) with a dedicated test database created per session
    and truncated between tests.
  - Real-send integration tests are skipped unless GMAIL_TEST_* env vars
    are configured (never send real email in CI without credentials).
"""
import os
import sys
import uuid

import pytest

ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
if ROOT not in sys.path:
    sys.path.insert(0, ROOT)

TEST_DB_NAME = f"bulkmailer_test_{uuid.uuid4().hex[:8]}"
POSTGRES_ADMIN_URL = os.environ.get(
    "TEST_DATABASE_ADMIN_URL",
    "postgresql+psycopg://mailer:mailer_dev_pw@localhost:5432/postgres",
)
TEST_DATABASE_URL = os.environ.get(
    "TEST_DATABASE_URL",
    f"postgresql+psycopg://mailer:mailer_dev_pw@localhost:5432/{TEST_DB_NAME}",
)


def _probe_postgres() -> bool:
    """True if a real PostgreSQL server is reachable for integration tests."""
    try:
        from sqlalchemy import create_engine, text

        eng = create_engine(POSTGRES_ADMIN_URL, pool_pre_ping=True)
        with eng.connect() as c:
            c.execute(text("SELECT 1"))
        eng.dispose()
        return True
    except Exception:
        return False


PG_AVAILABLE = _probe_postgres()


# ---------------------------------------------------------------------------
# Session-scoped: create the real test database, build app bound to it
# ---------------------------------------------------------------------------
@pytest.fixture(scope="session")
def test_app():
    if not PG_AVAILABLE:
        pytest.skip("PostgreSQL not available for integration tests")
    import importlib

    from sqlalchemy import create_engine, text

    # 1. create fresh test database
    admin = create_engine(POSTGRES_ADMIN_URL, pool_pre_ping=True, isolation_level="AUTOCOMMIT")
    with admin.connect() as c:
        c.execute(text(f'DROP DATABASE IF EXISTS "{TEST_DB_NAME}"'))
        c.execute(text(f'CREATE DATABASE "{TEST_DB_NAME}"'))
    admin.dispose()

    # 2. point settings at it and reload DB + main modules in dependency order
    import app.core.config as config_mod

    original_url = config_mod.settings.DATABASE_URL
    config_mod.settings.DATABASE_URL = TEST_DATABASE_URL

    import app.db.session as db_session_mod

    importlib.reload(db_session_mod)
    import app.main as main_mod

    importlib.reload(main_mod)

    # 3. create schema
    from app.models import Base

    eng = create_engine(TEST_DATABASE_URL, pool_pre_ping=True)
    Base.metadata.create_all(eng)
    eng.dispose()

    yield main_mod.app

    # 4. restore global modules and drop the test database
    config_mod.settings.DATABASE_URL = original_url
    importlib.reload(db_session_mod)
    admin = create_engine(POSTGRES_ADMIN_URL, pool_pre_ping=True, isolation_level="AUTOCOMMIT")
    with admin.connect() as c:
        c.execute(text(f'DROP DATABASE IF EXISTS "{TEST_DB_NAME}"'))
    admin.dispose()


# Tables to truncate between API tests (order-independent via TRUNCATE CASCADE)
_TRUNCATE = [
    "audit_logs", "email_events", "send_attempts", "send_jobs",
    "campaign_recipients", "campaigns", "recipients", "recipient_lists",
    "subject_lines", "html_templates", "attachments", "company_phone_numbers",
    "suppression_list", "oauth_tokens", "oauth_client_configs",
    "gmail_accounts", "application_settings", "users",
]


@pytest.fixture()
def api(test_app):
    """Fresh TestClient with a clean database and one registered+logged-in user."""
    from fastapi.testclient import TestClient
    from sqlalchemy import create_engine, text

    # wipe all data (keep schema)
    eng = create_engine(TEST_DATABASE_URL)
    with eng.connect() as c:
        for t in _TRUNCATE:
            try:
                c.execute(text(f'TRUNCATE TABLE "{t}" RESTART IDENTITY CASCADE'))
            except Exception:
                pass
        c.commit()
    eng.dispose()

    with TestClient(test_app) as client:
        em = f"t_{uuid.uuid4().hex[:10]}@example.com"
        pw = "TestPass#12345"
        r = client.post("/auth/register", json={"email": em, "password": pw, "full_name": "Tester"})
        assert r.status_code in (200, 201), f"register failed: {r.status_code} {r.text[:300]}"
        r = client.post("/auth/login", json={"email": em, "password": pw})
        assert r.status_code == 200, f"login failed: {r.status_code} {r.text[:300]}"
        client.email, client.password = em, pw
        yield client


@pytest.fixture(scope="session")
def db_engine(test_app):
    from sqlalchemy import create_engine

