#!/usr/bin/env python3
"""
Tests for GET /api/v1/history — HRT-81
Historique limité/illimité selon plan (Free/Premium/Pro)

Run with:
    cd /home/h3r7/turf_saas
    source venv/bin/activate
    python -m pytest tests/test_history.py -v
"""

import json
import os
import sys
import sqlite3
import tempfile
from datetime import datetime, timedelta

import pytest

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# Use an isolated temp DB for these tests
_tmp_db = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
_tmp_db.close()
os.environ["TURF_SAAS_DB"] = _tmp_db.name
os.environ["JWT_SECRET_KEY"] = "test-history-secret-key"

from app_v1 import create_app
from auth_db import init_auth_tables


# ──────────────────────────────────────────────────────────────
# Helpers
# ──────────────────────────────────────────────────────────────

TODAY = datetime.now().date()


def days_ago(n: int) -> str:
    return (TODAY - timedelta(days=n)).isoformat()


def auth_header(token: str) -> dict:
    return {"Authorization": f"Bearer {token}"}


# ──────────────────────────────────────────────────────────────
# Fixtures
# ──────────────────────────────────────────────────────────────


@pytest.fixture(scope="module")
def app():
    # Enforce this module s temp DB
    os.environ["TURF_SAAS_DB"] = _tmp_db.name
    os.environ["JWT_SECRET_KEY"] = "test-history-secret-key"
    application = create_app()
    application.config["TESTING"] = True
    application.config["JWT_SECRET_KEY"] = "test-history-secret-key"
    return application


@pytest.fixture(scope="module")
def client(app):
    return app.test_client()


@pytest.fixture(scope="module")
def seeded_db():
    """
    Seed the test DB:
    - Create ml_predictions_cache with rows spanning 120 days back
    - Create users for free/premium/pro plans
    """
    # Reset TURF_SAAS_DB to this module-s temp DB at runtime
    os.environ["TURF_SAAS_DB"] = _tmp_db.name
    db_path = _tmp_db.name

    # Ensure auth tables (users, refresh_tokens, subscriptions) exist in the test DB
    # init_auth_tables() is idempotent — safe to call even if tables already exist
    init_auth_tables()

    conn = sqlite3.connect(db_path)

    # Create ml_predictions_cache table if absent
    conn.execute("""
        CREATE TABLE IF NOT EXISTS ml_predictions_cache (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            date TEXT NOT NULL,
            horse_name TEXT,
            prob_top1 REAL,
            prob_top3 REAL,
            ml_score REAL,
            race_label TEXT,
            hippodrome TEXT,
            heure TEXT,
            is_value_bet INTEGER DEFAULT 0
        )
    """)

    # Seed rows at: 1, 6, 7, 8, 30, 89, 90, 91, 100 days ago
    offsets = [1, 6, 7, 8, 30, 89, 90, 91, 100]
    for offset in offsets:
        d = days_ago(offset)
        conn.execute(
            """INSERT INTO ml_predictions_cache
               (date, horse_name, prob_top1, prob_top3, ml_score, race_label, hippodrome, heure, is_value_bet)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
            (d, f"Cheval_{offset}j", 0.5, 0.8, 0.75, f"R1C1", "PARIS", "14:00", 0),
        )

    conn.commit()
    conn.close()
    return db_path


@pytest.fixture(scope="module")
def auth_tokens(client, seeded_db):
    """Register/login users for each plan and return their JWT tokens."""
    plans = {
        "free": "hist_free@test.com",
        "premium": "hist_premium@test.com",
        "pro": "hist_pro@test.com",
    }
    password = "password123"

    for plan, email in plans.items():
        r = client.post(
            "/api/v1/auth/register",
            json={"email": email, "password": password},
            content_type="application/json",
        )
        assert r.status_code in (201, 409), f"register failed for {plan}: {r.data}"

    # Set plan via direct DB
    # Reset TURF_SAAS_DB to this module-s temp DB at runtime
    os.environ["TURF_SAAS_DB"] = _tmp_db.name
    db_path = _tmp_db.name
    conn = sqlite3.connect(db_path)
    for plan, email in plans.items():
        conn.execute("UPDATE users SET plan = ? WHERE email = ?", (plan, email))
    conn.commit()
    conn.close()

    tokens = {}
    for plan, email in plans.items():
        r = client.post(
            "/api/v1/auth/login",
            json={"email": email, "password": password},
            content_type="application/json",
        )
        assert r.status_code == 200, f"login failed for {plan}: {r.data}"
        tokens[plan] = r.get_json()["access_token"]

    return tokens


# ──────────────────────────────────────────────────────────────
# Auth guard
# ──────────────────────────────────────────────────────────────


class TestHistoryAuth:
    def test_requires_auth(self, client):
        """Unauthenticated request must return 401."""
        r = client.get("/api/v1/history")
        assert r.status_code == 401

    def test_invalid_token_returns_401(self, client):
        r = client.get(
            "/api/v1/history",
            headers={"Authorization": "Bearer this.is.not.valid"},
        )
        assert r.status_code == 401


# ──────────────────────────────────────────────────────────────
# Free plan — 7-day window
# ──────────────────────────────────────────────────────────────


class TestHistoryFreePlan:
    def test_free_can_access_last_7_days(self, client, auth_tokens, seeded_db):
        """Free user: start = today-6 (within 7-day window) must return 200."""
        start = days_ago(6)
        r = client.get(
            f"/api/v1/history?start={start}&end={TODAY.isoformat()}",
            headers=auth_header(auth_tokens["free"]),
        )
        assert r.status_code == 200
        data = r.get_json()
        assert data["status"] == "ok"
        assert data["plan"] == "free"
        assert data["history_limit_days"] == 7

    def test_free_blocked_beyond_7_days(self, client, auth_tokens, seeded_db):
        """Free user: start = today-8 must return 403 (beyond 7-day window)."""
        start = days_ago(8)
        r = client.get(
            f"/api/v1/history?start={start}&end={TODAY.isoformat()}",
            headers=auth_header(auth_tokens["free"]),
        )
        assert r.status_code == 403
        data = r.get_json()
        assert data["code"] == 403
        assert (
            "upgrade" in data.get("message", "").lower()
            or "plan" in data.get("message", "").lower()
        )

    def test_free_default_request_returns_200(self, client, auth_tokens, seeded_db):
        """Free user: no dates specified — should use defaults and return 200."""
        r = client.get(
            "/api/v1/history",
            headers=auth_header(auth_tokens["free"]),
        )
        assert r.status_code == 200
        data = r.get_json()
        assert data["status"] == "ok"
        assert "history" in data
        assert "pagination" in data

    def test_free_upgrade_hint_in_403(self, client, auth_tokens, seeded_db):
        """403 response must contain required_plans and upgrade_url."""
        start = days_ago(30)
        r = client.get(
            f"/api/v1/history?start={start}",
            headers=auth_header(auth_tokens["free"]),
        )
        assert r.status_code == 403
        data = r.get_json()
        assert "required_plans" in data
        assert "upgrade_url" in data


# ──────────────────────────────────────────────────────────────
# Premium plan — 90-day window
# ──────────────────────────────────────────────────────────────


class TestHistoryPremiumPlan:
    def test_premium_can_access_within_90_days(self, client, auth_tokens, seeded_db):
        """Premium user: start = today-89 must return 200."""
        start = days_ago(89)
        r = client.get(
            f"/api/v1/history?start={start}&end={TODAY.isoformat()}",
            headers=auth_header(auth_tokens["premium"]),
        )
        assert r.status_code == 200
        data = r.get_json()
        assert data["status"] == "ok"
        assert data["plan"] == "premium"
        assert data["history_limit_days"] == 90

    def test_premium_blocked_beyond_90_days(self, client, auth_tokens, seeded_db):
        """Premium user: start = today-91 must return 403."""
        start = days_ago(91)
        r = client.get(
            f"/api/v1/history?start={start}&end={TODAY.isoformat()}",
            headers=auth_header(auth_tokens["premium"]),
        )
        assert r.status_code == 403
        data = r.get_json()
        assert data["code"] == 403
        assert "required_plans" in data
        # Premium upgrade hint should suggest pro
        assert "pro" in data.get("required_plans", [])

    def test_premium_can_access_last_7_days(self, client, auth_tokens, seeded_db):
        """Premium user can always access the free window too."""
        start = days_ago(6)
        r = client.get(
            f"/api/v1/history?start={start}",
            headers=auth_header(auth_tokens["premium"]),
        )
        assert r.status_code == 200


# ──────────────────────────────────────────────────────────────
# Pro plan — unlimited
# ──────────────────────────────────────────────────────────────


class TestHistoryProPlan:
    def test_pro_can_access_old_data(self, client, auth_tokens, seeded_db):
        """Pro user: start = today-100 must return 200 (unlimited)."""
        start = days_ago(100)
        r = client.get(
            f"/api/v1/history?start={start}&end={TODAY.isoformat()}",
            headers=auth_header(auth_tokens["pro"]),
        )
        assert r.status_code == 200
        data = r.get_json()
        assert data["status"] == "ok"
        assert data["plan"] == "pro"
        assert data["history_limit_days"] is None  # unlimited

    def test_pro_default_request_returns_200(self, client, auth_tokens, seeded_db):
        r = client.get(
            "/api/v1/history",
            headers=auth_header(auth_tokens["pro"]),
        )
        assert r.status_code == 200

    def test_pro_can_see_all_seeded_rows(self, client, auth_tokens, seeded_db):
        """Pro fetching entire seeded range (100 days) should get all inserted rows."""
        start = days_ago(100)
        end = TODAY.isoformat()
        r = client.get(
            f"/api/v1/history?start={start}&end={end}&limit=500",
            headers=auth_header(auth_tokens["pro"]),
        )
        assert r.status_code == 200
        data = r.get_json()
        # All 9 seeded rows should be present
        assert data["pagination"]["total"] == 9


# ──────────────────────────────────────────────────────────────
# Input validation
# ──────────────────────────────────────────────────────────────


class TestHistoryValidation:
    def test_invalid_start_format(self, client, auth_tokens):
        r = client.get(
            "/api/v1/history?start=31-12-2025",
            headers=auth_header(auth_tokens["pro"]),
        )
        assert r.status_code == 400
        data = r.get_json()
        assert data["code"] == 400
        assert "start" in data["message"].lower()

    def test_invalid_end_format(self, client, auth_tokens):
        r = client.get(
            "/api/v1/history?end=2025/12/31",
            headers=auth_header(auth_tokens["pro"]),
        )
        assert r.status_code == 400
        data = r.get_json()
        assert "end" in data["message"].lower()

    def test_start_after_end_returns_400(self, client, auth_tokens):
        r = client.get(
            f"/api/v1/history?start={TODAY.isoformat()}&end={days_ago(5)}",
            headers=auth_header(auth_tokens["pro"]),
        )
        assert r.status_code == 400

    def test_pagination_limit_respected(self, client, auth_tokens, seeded_db):
        start = days_ago(100)
        r = client.get(
            f"/api/v1/history?start={start}&limit=3&offset=0",
            headers=auth_header(auth_tokens["pro"]),
        )
        assert r.status_code == 200
        data = r.get_json()
        assert len(data["history"]) <= 3
        assert data["pagination"]["limit"] == 3

    def test_pagination_has_more(self, client, auth_tokens, seeded_db):
        """has_more should be True when more rows exist beyond current page."""
        start = days_ago(100)
        r = client.get(
            f"/api/v1/history?start={start}&limit=3&offset=0",
            headers=auth_header(auth_tokens["pro"]),
        )
        assert r.status_code == 200
        data = r.get_json()
        # 9 total rows seeded, limit=3 → has_more=True
        assert data["pagination"]["has_more"] is True

    def test_response_shape(self, client, auth_tokens, seeded_db):
        """Verify the full response envelope shape."""
        r = client.get(
            "/api/v1/history",
            headers=auth_header(auth_tokens["pro"]),
        )
        assert r.status_code == 200
        data = r.get_json()
        assert "status" in data
        assert "plan" in data
        assert "history_limit_days" in data
        assert "start" in data
        assert "end" in data
        assert "history" in data
        assert "pagination" in data
        pagination = data["pagination"]
        assert "total" in pagination
        assert "limit" in pagination
        assert "offset" in pagination
        assert "has_more" in pagination

    def test_history_row_fields(self, client, auth_tokens, seeded_db):
        """Each history row must contain the expected ML fields."""
        start = days_ago(10)
        r = client.get(
            f"/api/v1/history?start={start}&limit=5",
            headers=auth_header(auth_tokens["pro"]),
        )
        assert r.status_code == 200
        data = r.get_json()
        if data["history"]:
            row = data["history"][0]
            expected_fields = {
                "id",
                "date",
                "horse_name",
                "prob_top1",
                "prob_top3",
                "ml_score",
                "race_label",
                "hippodrome",
                "heure",
                "is_value_bet",
            }
            assert expected_fields.issubset(set(row.keys()))
