# app/modules/tenancy/tests/integration/test_store_password_reset.py
"""
Integration tests for store user password reset flow.

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

import uuid

import pytest

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

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

BASE = "/api/v1/store/auth"


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

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


@pytest.fixture
def spr_store(db, spr_merchant):
    """Create a store for password reset tests."""
    uid = uuid.uuid4().hex[:6].upper()
    store = Store(
        merchant_id=spr_merchant.id,
        store_code=f"SPR{uid}",
        subdomain=f"spr-{uid.lower()}",
        name="Store PW Reset Test Store",
        is_active=True,
        is_verified=True,
    )
    db.add(store)
    db.commit()
    db.refresh(store)
    return store


@pytest.fixture
def spr_store_user(db, spr_owner, spr_store):
    """Associate the owner with the store."""
    store_user = StoreUser(
        user_id=spr_owner.id,
        store_id=spr_store.id,
        is_active=True,
    )
    db.add(store_user)
    db.commit()
    db.refresh(store_user)
    return store_user


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


class TestStoreForgotPassword:
    """Tests for POST /api/v1/store/auth/forgot-password."""

    def test_forgot_password_success(self, client, spr_owner):
        """Valid email returns success message."""
        response = client.post(
            f"{BASE}/forgot-password",
            json={"email": spr_owner.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": "nonexistent_store@test.com"},
        )
        assert response.status_code == 200
        data = response.json()
        assert "message" in data


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


class TestStoreResetPassword:
    """Tests for POST /api/v1/store/auth/reset-password."""

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

        response = client.post(
            f"{BASE}/reset-password",
            json={
                "token": plaintext,
                "new_password": "newstorepass456",  # 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": spr_owner.username,
                "password": "newstorepass456",  # noqa: SEC001
                "store_code": spr_store.store_code,
            },
        )
        assert login_resp.status_code == 200
        assert "access_token" in login_resp.json()

    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": "newstorepass456",  # 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, spr_owner):
        """Expired token returns error."""
        from datetime import datetime, timedelta

        plaintext = UserPasswordResetToken.create_for_user(db, spr_owner.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": "newstorepass456",  # noqa: SEC001
            },
        )
        assert response.status_code == 401

    def test_reset_password_used_token(self, client, db, spr_owner):
        """Already-used token returns error."""
        plaintext = UserPasswordResetToken.create_for_user(db, spr_owner.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": "newstorepass456",  # noqa: SEC001
            },
        )
        assert response.status_code == 401
