# app/modules/tenancy/tests/integration/test_merchant_password_reset.py
"""
Integration tests for merchant password reset flow.

Tests:
- POST /api/v1/merchants/auth/forgot-password
- POST /api/v1/merchants/auth/reset-password
"""

import uuid

import pytest

from app.modules.tenancy.models import Merchant, User
from app.modules.tenancy.models.user_password_reset_token import UserPasswordResetToken

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

BASE = "/api/v1/merchants/auth"


@pytest.fixture
def mpr_user(db):
    """Create a merchant owner user for password reset tests."""
    from middleware.auth import AuthManager

    auth = AuthManager()
    uid = uuid.uuid4().hex[:8]
    user = User(
        email=f"mpr_{uid}@test.com",
        username=f"mpr_{uid}",
        hashed_password=auth.hash_password("oldpass123"),  # 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 mpr_merchant(db, mpr_user):
    """Create a merchant for the test user."""
    merchant = Merchant(
        name="Password Reset Test Merchant",
        owner_user_id=mpr_user.id,
        contact_email=mpr_user.email,
        is_active=True,
        is_verified=True,
    )
    db.add(merchant)
    db.commit()
    db.refresh(merchant)
    return merchant


@pytest.fixture
def mpr_inactive_user(db):
    """Create an inactive user."""
    from middleware.auth import AuthManager

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


# ============================================================================
# Forgot Password Tests
# ============================================================================


class TestMerchantForgotPassword:
    """Tests for POST /api/v1/merchants/auth/forgot-password."""

    def test_forgot_password_success(self, client, mpr_user):
        """Valid email returns success message."""
        response = client.post(
            f"{BASE}/forgot-password",
            json={"email": mpr_user.email},
        )
        assert response.status_code == 200
        data = response.json()
        assert "message" in data
        assert "password reset link" in data["message"]

    def test_forgot_password_nonexistent_email(self, client):
        """Non-existent email still returns success (anti-enumeration)."""
        response = client.post(
            f"{BASE}/forgot-password",
            json={"email": "doesnotexist@test.com"},
        )
        assert response.status_code == 200
        data = response.json()
        assert "message" in data

    def test_forgot_password_inactive_user(self, client, mpr_inactive_user):
        """Inactive user email still returns success (anti-enumeration)."""
        response = client.post(
            f"{BASE}/forgot-password",
            json={"email": mpr_inactive_user.email},
        )
        assert response.status_code == 200
        data = response.json()
        assert "message" in data


# ============================================================================
# Reset Password Tests
# ============================================================================


class TestMerchantResetPassword:
    """Tests for POST /api/v1/merchants/auth/reset-password."""

    def test_reset_password_success(self, client, db, mpr_user, mpr_merchant):
        """Valid token resets password and allows login with new password."""
        plaintext = UserPasswordResetToken.create_for_user(db, mpr_user.id)
        db.commit()

        response = client.post(
            f"{BASE}/reset-password",
            json={
                "token": plaintext,
                "new_password": "newpass456",  # noqa: SEC001
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert "Password reset successfully" in data["message"]

        # Verify can login with new password
        login_resp = client.post(
            f"{BASE}/login",
            json={
                "email_or_username": mpr_user.username,
                "password": "newpass456",  # noqa: SEC001
            },
        )
        assert login_resp.status_code == 200
        assert "access_token" in login_resp.json()

    def test_reset_password_old_password_fails(self, client, db, mpr_user, mpr_merchant):
        """After reset, old password should not work."""
        plaintext = UserPasswordResetToken.create_for_user(db, mpr_user.id)
        db.commit()

        client.post(
            f"{BASE}/reset-password",
            json={
                "token": plaintext,
                "new_password": "newpass456",  # noqa: SEC001
            },
        )

        # Old password should fail
        login_resp = client.post(
            f"{BASE}/login",
            json={
                "email_or_username": mpr_user.username,
                "password": "oldpass123",  # noqa: SEC001
            },
        )
        assert login_resp.status_code == 401

    def test_reset_password_invalid_token(self, client):
        """Invalid token returns error."""
        response = client.post(
            f"{BASE}/reset-password",
            json={
                "token": "invalid-token-value",
                "new_password": "newpass456",  # noqa: SEC001
            },
        )
        assert response.status_code == 401
        data = response.json()
        assert data.get("error_code") == "INVALID_TOKEN"

    def test_reset_password_expired_token(self, client, db, mpr_user):
        """Expired token returns error."""
        from datetime import datetime, timedelta

        plaintext = UserPasswordResetToken.create_for_user(db, mpr_user.id)
        db.commit()

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

        response = client.post(
            f"{BASE}/reset-password",
            json={
                "token": plaintext,
                "new_password": "newpass456",  # noqa: SEC001
            },
        )
        assert response.status_code == 401

    def test_reset_password_used_token(self, client, db, mpr_user):
        """Already-used token returns error."""
        plaintext = UserPasswordResetToken.create_for_user(db, mpr_user.id)
        db.commit()

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

        response = client.post(
            f"{BASE}/reset-password",
            json={
                "token": plaintext,
                "new_password": "newpass456",  # noqa: SEC001
            },
        )
        assert response.status_code == 401
