"""Tests for the ConsentLog model — B-2.1 Loi 25 audit trail."""
import os
import sys
from datetime import datetime, timedelta

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
os.environ.setdefault('SQLALCHEMY_DATABASE_URI', 'sqlite:///:memory:')
os.environ.setdefault('SECRET_KEY', 'test-secret-key')

from src.app import app, db  # noqa: E402
from src.models.user import User  # noqa: E402
from src.models.consent import ConsentLog  # noqa: E402


def _make_user(username='alice', email='alice@example.com'):
    user = User(username=username, email=email, password='x' * 60)
    db.session.add(user)
    db.session.commit()
    return user


def test_consent_log_records_cgu_grant():
    """Granting CGU consent at signup creates a row with all audit fields."""
    with app.app_context():
        db.create_all()
        try:
            user = _make_user()
            log = ConsentLog(
                user_id=user.id,
                consent_type='cgu',
                version='1.0',
                granted=True,
                ip_address='192.0.2.1',
                user_agent='Mozilla/5.0 (Windows NT 10.0)'
            )
            db.session.add(log)
            db.session.commit()
            assert ConsentLog.query.count() == 1
            assert log.granted_at is not None
            assert log.granted_at <= datetime.utcnow()
            assert log.revoked_at is None
        finally:
            db.session.rollback()
            db.drop_all()


def test_consent_log_supports_4_consent_types():
    """All 4 Loi 25 consent types (cgu, confidentialite, marketing, analytics) are valid."""
    with app.app_context():
        db.create_all()
        try:
            user = _make_user()
            for consent_type in ('cgu', 'confidentialite', 'marketing', 'analytics'):
                log = ConsentLog(
                    user_id=user.id,
                    consent_type=consent_type,
                    version='1.0',
                    granted=True,
                    ip_address='192.0.2.1',
                    user_agent='Mozilla/5.0'
                )
                db.session.add(log)
            db.session.commit()
            types = sorted([l.consent_type for l in ConsentLog.query.all()])
            assert types == ['analytics', 'cgu', 'confidentialite', 'marketing']
        finally:
            db.session.rollback()
            db.drop_all()


def test_consent_log_revoke_creates_separate_row():
    """Revoking later does NOT mutate the grant — both rows persist for audit trail."""
    with app.app_context():
        db.create_all()
        try:
            user = _make_user()
            grant = ConsentLog(
                user_id=user.id, consent_type='marketing', version='1.0',
                granted=True, ip_address='192.0.2.1', user_agent='UA'
            )
            db.session.add(grant)
            db.session.commit()

            revoke = ConsentLog(
                user_id=user.id, consent_type='marketing', version='1.0',
                granted=False, ip_address='192.0.2.1', user_agent='UA',
                revoked_at=datetime.utcnow()
            )
            db.session.add(revoke)
            db.session.commit()

            rows = ConsentLog.query.filter_by(consent_type='marketing').order_by(ConsentLog.id).all()
            assert len(rows) == 2, "Grant and revoke must each have their own row (audit trail)"
            assert rows[0].granted is True and rows[0].revoked_at is None
            assert rows[1].granted is False and rows[1].revoked_at is not None
        finally:
            db.session.rollback()
            db.drop_all()


def test_consent_log_user_backref():
    """User.consent_logs backref returns the user's consent history."""
    with app.app_context():
        db.create_all()
        try:
            user = _make_user()
            for ct in ('cgu', 'confidentialite'):
                db.session.add(ConsentLog(
                    user_id=user.id, consent_type=ct, version='1.0',
                    granted=True, ip_address='192.0.2.1', user_agent='UA'
                ))
            db.session.commit()
            assert len(user.consent_logs) == 2
            assert sorted([l.consent_type for l in user.consent_logs]) == ['cgu', 'confidentialite']
        finally:
            db.session.rollback()
            db.drop_all()


def test_consent_log_requires_ip_and_user_agent():
    """ip_address and user_agent are NOT NULL — required for Loi 25 traceability."""
    from sqlalchemy.exc import IntegrityError
    with app.app_context():
        db.create_all()
        try:
            user = _make_user(username='erica', email='erica@example.com')

            # Missing ip_address
            log_no_ip = ConsentLog(
                user_id=user.id, consent_type='cgu', version='1.0',
                granted=True, user_agent='UA'
            )
            db.session.add(log_no_ip)
            try:
                db.session.commit()
                raise AssertionError("Expected IntegrityError on missing ip_address")
            except IntegrityError:
                db.session.rollback()

            # Missing user_agent
            log_no_ua = ConsentLog(
                user_id=user.id, consent_type='cgu', version='1.0',
                granted=True, ip_address='192.0.2.1'
            )
            db.session.add(log_no_ua)
            try:
                db.session.commit()
                raise AssertionError("Expected IntegrityError on missing user_agent")
            except IntegrityError:
                db.session.rollback()
        finally:
            db.session.rollback()
            db.drop_all()


def test_user_has_new_b21_fields():
    """User model gained: totp_secret_encrypted, totp_enabled, webauthn_credentials, ordre_pro, cabinet, stripe_customer_id, subscription_status."""
    with app.app_context():
        db.create_all()
        try:
            user = _make_user()
            user.totp_secret_encrypted = 'gAAAAABh-encrypted-fernet-token-placeholder'
            user.totp_enabled = True
            user.webauthn_credentials = [{'id': 'cred1', 'public_key': 'abc'}]
            user.ordre_pro = 'barreau'
            user.cabinet = 'Cabinet Pilote A'
            user.stripe_customer_id = 'cus_TestCustomerId'
            user.subscription_status = 'active'
            db.session.commit()

            fetched = User.query.filter_by(username='alice').first()
            assert fetched.totp_secret_encrypted == 'gAAAAABh-encrypted-fernet-token-placeholder'
            assert fetched.totp_enabled is True
            assert fetched.webauthn_credentials == [{'id': 'cred1', 'public_key': 'abc'}]
            assert fetched.ordre_pro == 'barreau'
            assert fetched.cabinet == 'Cabinet Pilote A'
            assert fetched.stripe_customer_id == 'cus_TestCustomerId'
            assert fetched.subscription_status == 'active'
        finally:
            db.session.rollback()
            db.drop_all()


def test_user_b21_fields_default_to_safe_values():
    """New User defaults: totp_enabled=False, others None."""
    with app.app_context():
        db.create_all()
        try:
            user = _make_user(username='bob', email='bob@example.com')
            assert user.totp_enabled is False, "totp_enabled must default to False (no MFA bypass)"
            assert user.totp_secret_encrypted is None
            assert user.webauthn_credentials is None
            assert user.stripe_customer_id is None
            assert user.subscription_status is None
            assert user.ordre_pro is None
            assert user.cabinet is None
        finally:
            db.session.rollback()
            db.drop_all()


def test_consent_log_survives_user_deletion_with_null_user_id():
    """Loi 25 art. 28.1 right-to-erasure: deleting a User must NOT delete their
    consent log rows. The user_id is set to NULL, the audit row survives.
    """
    with app.app_context():
        db.create_all()
        try:
            user = _make_user(username='claire', email='claire@example.com')
            uid = user.id
            log = ConsentLog(
                user_id=uid, consent_type='cgu', version='2026-04-27',
                granted=True, ip_address='192.0.2.1', user_agent='UA'
            )
            db.session.add(log)
            db.session.commit()
            log_id = log.id

            db.session.delete(user)
            db.session.commit()

            # Audit row must survive
            surviving = ConsentLog.query.get(log_id)
            assert surviving is not None, "Consent log row must survive user deletion (Loi 25 audit trail)"
            assert surviving.user_id is None, "user_id must be NULL after user deletion (data minimization)"
            assert surviving.consent_type == 'cgu'
            assert surviving.granted is True
        finally:
            db.session.rollback()
            db.drop_all()


def test_consent_log_rejects_invalid_consent_type():
    """Typos like 'comfidentialite' must be rejected at ORM level."""
    with app.app_context():
        db.create_all()
        try:
            user = _make_user(username='diane', email='diane@example.com')
            try:
                ConsentLog(
                    user_id=user.id, consent_type='comfidentialite',  # typo
                    version='1.0', granted=True,
                    ip_address='192.0.2.1', user_agent='UA'
                )
                raise AssertionError("ValueError expected on invalid consent_type")
            except ValueError as e:
                assert 'Invalid consent_type' in str(e)
        finally:
            db.session.rollback()
            db.drop_all()
