"""
Shared test fixtures for the BeanFlows test suite.
"""
import hashlib
import hmac
from datetime import datetime
from pathlib import Path
from unittest.mock import AsyncMock, patch

import aiosqlite
import pytest

from beanflows import analytics, core
from beanflows.app import create_app

SCHEMA_PATH = Path(__file__).parent.parent / "src" / "beanflows" / "migrations" / "schema.sql"


# ── Database ─────────────────────────────────────────────────

@pytest.fixture
async def db():
    """In-memory SQLite with full schema, patches core._db."""
    conn = await aiosqlite.connect(":memory:")
    conn.row_factory = aiosqlite.Row
    await conn.execute("PRAGMA foreign_keys=ON")

    schema = SCHEMA_PATH.read_text()
    await conn.executescript(schema)
    await conn.commit()

    original_db = core._db
    core._db = conn

    yield conn

    core._db = original_db
    await conn.close()


# ── App & client ─────────────────────────────────────────────

@pytest.fixture
async def app(db):
    """Quart app with DB already initialized (init_db/close_db patched to no-op)."""
    with patch.object(core, "init_db", new_callable=AsyncMock), \
         patch.object(core, "close_db", new_callable=AsyncMock), \
         patch.object(analytics, "open_analytics_db"), \
         patch.object(analytics, "close_analytics_db"):
        application = create_app()
        application.config["TESTING"] = True
        yield application


@pytest.fixture
async def client(app):
    """Unauthenticated test client."""
    async with app.test_client() as c:
        yield c


# ── Users ────────────────────────────────────────────────────

@pytest.fixture
async def test_user(db):
    """Create a test user, return dict with id/email/name."""
    now = datetime.utcnow().isoformat()
    async with db.execute(
        "INSERT INTO users (email, name, created_at) VALUES (?, ?, ?)",
        ("test@example.com", "Test User", now),
    ) as cursor:
        user_id = cursor.lastrowid
    await db.commit()
    return {"id": user_id, "email": "test@example.com", "name": "Test User"}


@pytest.fixture
async def auth_client(app, test_user):
    """Test client with session['user_id'] pre-set."""
    async with app.test_client() as c:
        async with c.session_transaction() as sess:
            sess["user_id"] = test_user["id"]
        yield c


# ── Subscriptions ────────────────────────────────────────────

@pytest.fixture
def create_subscription(db):
    """Factory: create a subscription row for a user."""
    async def _create(
        user_id: int,
        plan: str = "pro",
        status: str = "active",

        paddle_customer_id: str = "ctm_test123",
        paddle_subscription_id: str = "sub_test456",

        current_period_end: str = "2025-03-01T00:00:00Z",
    ) -> int:
        now = datetime.utcnow().isoformat()
        async with db.execute(

            """INSERT INTO subscriptions
               (user_id, plan, status, paddle_customer_id,
                paddle_subscription_id, current_period_end, created_at, updated_at)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
            (user_id, plan, status, paddle_customer_id, paddle_subscription_id,
             current_period_end, now, now),

        ) as cursor:
            sub_id = cursor.lastrowid
        await db.commit()
        return sub_id
    return _create


# ── Config ───────────────────────────────────────────────────

@pytest.fixture(autouse=True)
def patch_config():

    """Set test Paddle config values."""

    original_values = {}
    test_values = {

        "PADDLE_API_KEY": "test_api_key_123",
        "PADDLE_WEBHOOK_SECRET": "whsec_test_secret",
        "PADDLE_PRICES": {"starter": "pri_starter_123", "pro": "pri_pro_456"},

        "BASE_URL": "http://localhost:5000",
        "DEBUG": True,
    }
    for key, val in test_values.items():
        original_values[key] = getattr(core.config, key, None)
        setattr(core.config, key, val)


    yield


    for key, val in original_values.items():
        setattr(core.config, key, val)


# ── Webhook helpers ──────────────────────────────────────────


def make_webhook_payload(
    event_type: str,
    subscription_id: str = "sub_test456",
    customer_id: str = "ctm_test123",
    user_id: str = "1",
    plan: str = "starter",
    status: str = "active",
    ends_at: str = "2025-03-01T00:00:00.000000Z",
) -> dict:
    """Build a Paddle webhook payload dict."""
    return {
        "event_type": event_type,
        "data": {
            "id": subscription_id,
            "status": status,
            "customer_id": customer_id,
            "custom_data": {"user_id": user_id, "plan": plan},
            "current_billing_period": {
                "starts_at": "2025-02-01T00:00:00.000000Z",
                "ends_at": ends_at,
            },
        },
    }


def sign_payload(payload_bytes: bytes, secret: str = "whsec_test_secret") -> str:
    """Compute HMAC-SHA256 signature for a webhook payload."""
    return hmac.new(secret.encode(), payload_bytes, hashlib.sha256).hexdigest()


# ── Analytics mock data ──────────────────────────────────────

MOCK_TIME_SERIES = [
    {"market_year": 2018, "Production": 165000, "Exports": 115000, "Imports": 105000,
     "Ending_Stocks": 33000, "Total_Distribution": 160000},
    {"market_year": 2019, "Production": 168000, "Exports": 118000, "Imports": 108000,
     "Ending_Stocks": 34000, "Total_Distribution": 163000},
    {"market_year": 2020, "Production": 170000, "Exports": 120000, "Imports": 110000,
     "Ending_Stocks": 35000, "Total_Distribution": 165000},
    {"market_year": 2021, "Production": 175000, "Exports": 125000, "Imports": 115000,
     "Ending_Stocks": 36000, "Total_Distribution": 170000},
    {"market_year": 2022, "Production": 172000, "Exports": 122000, "Imports": 112000,
     "Ending_Stocks": 34000, "Total_Distribution": 168000},
]

MOCK_TOP_COUNTRIES = [
    {"country_name": "Brazil", "country_code": "BR", "market_year": 2022, "Production": 65000},
    {"country_name": "Vietnam", "country_code": "VN", "market_year": 2022, "Production": 30000},
    {"country_name": "Colombia", "country_code": "CO", "market_year": 2022, "Production": 14000},
]

MOCK_STU_TREND = [
    {"market_year": 2020, "Stock_to_Use_Ratio_pct": 21.2},
    {"market_year": 2021, "Stock_to_Use_Ratio_pct": 21.1},
    {"market_year": 2022, "Stock_to_Use_Ratio_pct": 20.2},
]

MOCK_BALANCE = [
    {"market_year": 2020, "Production": 170000, "Total_Distribution": 165000, "Supply_Demand_Balance": 5000},
    {"market_year": 2021, "Production": 175000, "Total_Distribution": 170000, "Supply_Demand_Balance": 5000},
    {"market_year": 2022, "Production": 172000, "Total_Distribution": 168000, "Supply_Demand_Balance": 4000},
]

MOCK_YOY = [
    {"country_name": "Brazil", "country_code": "BR", "market_year": 2022,
     "Production": 65000, "Production_YoY_pct": -3.5},
    {"country_name": "Vietnam", "country_code": "VN", "market_year": 2022,
     "Production": 30000, "Production_YoY_pct": 2.1},
]

MOCK_COMMODITIES = [
    {"commodity_code": 711100, "commodity_name": "Coffee, Green"},
    {"commodity_code": 222000, "commodity_name": "Soybeans"},
]


@pytest.fixture
def mock_analytics():
    """Patch all analytics query functions with mock data."""
    with patch.object(analytics, "get_global_time_series", new_callable=AsyncMock,
                      return_value=MOCK_TIME_SERIES), \
         patch.object(analytics, "get_top_countries", new_callable=AsyncMock,
                      return_value=MOCK_TOP_COUNTRIES), \
         patch.object(analytics, "get_stock_to_use_trend", new_callable=AsyncMock,
                      return_value=MOCK_STU_TREND), \
         patch.object(analytics, "get_supply_demand_balance", new_callable=AsyncMock,
                      return_value=MOCK_BALANCE), \
         patch.object(analytics, "get_production_yoy_by_country", new_callable=AsyncMock,
                      return_value=MOCK_YOY), \
         patch.object(analytics, "get_country_comparison", new_callable=AsyncMock,
                      return_value=[]), \
         patch.object(analytics, "get_available_commodities", new_callable=AsyncMock,
                      return_value=MOCK_COMMODITIES), \
         patch.object(analytics, "fetch_analytics", new_callable=AsyncMock,
                      return_value=[{"result": 1}]):
        yield


