# app/modules/tenancy/tests/integration/test_email_verification_routes.py
"""
Integration tests for email verification routes.

Tests:
- GET /verify-email?token={token}  (page route)
- POST /api/v1/merchants/resend-verification  (API route)
- Login blocked for unverified email
"""

import uuid

import pytest

from app.modules.tenancy.models import Merchant, User
from app.modules.tenancy.models.email_verification_token import EmailVerificationToken

# ============================================================================
# Fixtures
# ============================================================================

MERCHANT_AUTH_BASE = "/api/v1/merchants/auth"
RESEND_BASE = "/api/v1/merchants/resend-verification"


@pytest.fixture
def ev_unverified_user(db):
    """Create an unverified user for email verification tests."""
    from middleware.auth import AuthManager

    auth = AuthManager()
    uid = uuid.uuid4().hex[:8]
    user = User(
        email=f"unverified_{uid}@test.com",
        username=f"unverified_{uid}",
        hashed_password=auth.hash_password("testpass123"),  # noqa: SEC001
        role="merchant_owner",
        is_active=True,
        is_email_verified=False,
    )
    db.add(user)
    db.commit()
    db.refresh(user)
    return user


@pytest.fixture
def ev_verified_user(db):
    """Create a verified user for comparison tests."""
    from middleware.auth import AuthManager

    auth = AuthManager()
    uid = uuid.uuid4().hex[:8]
    user = User(
        email=f"verified_{uid}@test.com",
        username=f"verified_{uid}",
        hashed_password=auth.hash_password("testpass123"),  # noqa: SEC001
        role="merchant_owner",
        is_active=True,
        is_email_verified=True,
    )
    db.add(user)
    db.commit()
    db.refresh(user)
    return user


@pytest.fixture
def ev_merchant(db, ev_verified_user):
    """Create a merchant for the verified user."""
    merchant = Merchant(
        name="Verification Test Merchant",
        owner_user_id=ev_verified_user.id,
        contact_email=ev_verified_user.email,
        is_active=True,
        is_verified=True,
    )
    db.add(merchant)
    db.commit()
    db.refresh(merchant)
    return merchant


@pytest.fixture
def ev_unverified_merchant(db, ev_unverified_user):
    """Create a merchant for the unverified user."""
    merchant = Merchant(
        name="Unverified Owner Merchant",
        owner_user_id=ev_unverified_user.id,
        contact_email=ev_unverified_user.email,
        is_active=True,
        is_verified=True,
    )
    db.add(merchant)
    db.commit()
    db.refresh(merchant)
    return merchant


# ============================================================================
# GET /verify-email Tests
# ============================================================================


class TestVerifyEmailPage:
    """Tests for GET /verify-email?token={token}."""

    def test_verify_email_success(self, client, db, ev_unverified_user):
        """Valid token verifies the user's email."""
        plaintext = EmailVerificationToken.create_for_user(db, ev_unverified_user.id)
        db.commit()

        response = client.get(f"/verify-email?token={plaintext}")
        assert response.status_code == 200
        assert "Email Verified" in response.text

        # User should now be verified
        db.refresh(ev_unverified_user)
        assert ev_unverified_user.is_email_verified is True

    def test_verify_email_invalid_token(self, client):
        """Invalid token shows error page."""
        response = client.get("/verify-email?token=invalid-token-value")
        assert response.status_code == 400
        assert "Verification Failed" in response.text

    def test_verify_email_expired_token(self, client, db, ev_unverified_user):
        """Expired token shows error page."""
        from datetime import datetime, timedelta

        plaintext = EmailVerificationToken.create_for_user(db, ev_unverified_user.id)
        db.commit()

        # Manually expire the token
        token = EmailVerificationToken.find_valid_token(db, plaintext)
        token.expires_at = datetime.utcnow() - timedelta(hours=1)
        db.commit()

        response = client.get(f"/verify-email?token={plaintext}")
        assert response.status_code == 400
        assert "Verification Failed" in response.text

    def test_verify_email_used_token(self, client, db, ev_unverified_user):
        """Already-used token shows error page."""
        plaintext = EmailVerificationToken.create_for_user(db, ev_unverified_user.id)
        db.commit()

        # Use the token first
        token = EmailVerificationToken.find_valid_token(db, plaintext)
        token.mark_used(db)
        db.commit()

        response = client.get(f"/verify-email?token={plaintext}")
        assert response.status_code == 400
        assert "Verification Failed" in response.text

    def test_verify_email_missing_token_param(self, client):
        """Missing token parameter returns 422."""
        response = client.get("/verify-email")
        assert response.status_code == 422


# ============================================================================
# POST /resend-verification Tests
# ============================================================================


class TestResendVerification:
    """Tests for POST /api/v1/merchants/resend-verification."""

    def test_resend_always_returns_success(self, client, ev_unverified_user):
        """Endpoint always returns success (anti-enumeration)."""
        response = client.post(
            RESEND_BASE,
            json={"email": ev_unverified_user.email},
        )
        assert response.status_code == 200
        data = response.json()
        assert "message" in data
        assert "verification link has been sent" in data["message"]

    def test_resend_nonexistent_email(self, client):
        """Non-existent email still returns success (anti-enumeration)."""
        response = client.post(
            RESEND_BASE,
            json={"email": "nonexistent@test.com"},
        )
        assert response.status_code == 200
        data = response.json()
        assert "message" in data

    def test_resend_already_verified_email(self, client, ev_verified_user):
        """Already-verified email returns success but doesn't send."""
        response = client.post(
            RESEND_BASE,
            json={"email": ev_verified_user.email},
        )
        assert response.status_code == 200
        data = response.json()
        assert "message" in data


# ============================================================================
# Login Blocked for Unverified Email Tests
# ============================================================================


class TestLoginBlockedUnverifiedEmail:
    """Tests that login is blocked for users with unverified email."""

    def test_merchant_login_blocked_unverified(self, client, ev_unverified_user, ev_unverified_merchant):
        """Merchant login should fail with EMAIL_NOT_VERIFIED for unverified user."""
        response = client.post(
            f"{MERCHANT_AUTH_BASE}/login",
            json={
                "email_or_username": ev_unverified_user.username,
                "password": "testpass123",  # noqa: SEC001
            },
        )
        assert response.status_code == 403
        data = response.json()
        assert data.get("error_code") == "EMAIL_NOT_VERIFIED"

    def test_merchant_login_allowed_verified(self, client, ev_verified_user, ev_merchant):
        """Merchant login should succeed for verified user."""
        response = client.post(
            f"{MERCHANT_AUTH_BASE}/login",
            json={
                "email_or_username": ev_verified_user.username,
                "password": "testpass123",  # noqa: SEC001
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert "access_token" in data

    def test_verify_then_login(self, client, db, ev_unverified_user, ev_unverified_merchant):
        """After verifying email, login should succeed."""
        # Create verification token and verify
        plaintext = EmailVerificationToken.create_for_user(db, ev_unverified_user.id)
        db.commit()

        verify_response = client.get(f"/verify-email?token={plaintext}")
        assert verify_response.status_code == 200

        # Now login should work
        login_response = client.post(
            f"{MERCHANT_AUTH_BASE}/login",
            json={
                "email_or_username": ev_unverified_user.username,
                "password": "testpass123",  # noqa: SEC001
            },
        )
        assert login_response.status_code == 200
        assert "access_token" in login_response.json()
