# app/modules/billing/tests/unit/test_signup_service.py
"""Unit tests for SignupService (simplified 3-step signup)."""

import uuid
from unittest.mock import patch

import pytest

from app.exceptions import (
    ConflictException,
    ResourceNotFoundException,
    ValidationException,
)
from app.modules.billing.models import TierCode
from app.modules.billing.services.signup_service import SignupService, _signup_sessions


@pytest.fixture(autouse=True)
def _clear_sessions():
    """Clear in-memory signup sessions before each test."""
    _signup_sessions.clear()
    yield
    _signup_sessions.clear()


@pytest.mark.unit
@pytest.mark.billing
class TestSignupServiceSession:
    """Tests for SignupService session management."""

    def setup_method(self):
        self.service = SignupService()

    def test_create_session_stores_language(self):
        """create_session stores the user's browsing language."""
        session_id = self.service.create_session(
            tier_code=TierCode.ESSENTIAL.value,
            is_annual=False,
            platform_code="loyalty",
            language="de",
        )

        session = self.service.get_session(session_id)
        assert session is not None
        assert session["language"] == "de"

    def test_create_session_default_language_fr(self):
        """create_session defaults to French when no language provided."""
        session_id = self.service.create_session(
            tier_code=TierCode.ESSENTIAL.value,
            is_annual=False,
            platform_code="oms",
        )

        session = self.service.get_session(session_id)
        assert session["language"] == "fr"

    def test_create_session_stores_platform_code(self):
        """create_session stores the platform code."""
        session_id = self.service.create_session(
            tier_code=TierCode.PROFESSIONAL.value,
            is_annual=True,
            platform_code="loyalty",
            language="en",
        )

        session = self.service.get_session(session_id)
        assert session["platform_code"] == "loyalty"
        assert session["is_annual"] is True
        assert session["tier_code"] == TierCode.PROFESSIONAL.value

    def test_create_session_raises_without_platform_code(self):
        """create_session raises ValidationException when platform_code is empty."""
        with pytest.raises(ValidationException):
            self.service.create_session(
                tier_code=TierCode.ESSENTIAL.value,
                is_annual=False,
                platform_code="",
            )

    def test_get_session_or_raise_missing(self):
        """get_session_or_raise raises ResourceNotFoundException for invalid session."""
        with pytest.raises(ResourceNotFoundException):
            self.service.get_session_or_raise("nonexistent_session_id")

    def test_delete_session(self):
        """delete_session removes the session from storage."""
        session_id = self.service.create_session(
            tier_code=TierCode.ESSENTIAL.value,
            is_annual=False,
            platform_code="oms",
        )
        assert self.service.get_session(session_id) is not None

        self.service.delete_session(session_id)
        assert self.service.get_session(session_id) is None


@pytest.mark.unit
@pytest.mark.billing
class TestSignupServiceAccountCreation:
    """Tests for SignupService.create_account (merged store creation)."""

    def setup_method(self):
        self.service = SignupService()

    def test_create_account_creates_store(self, db):
        """create_account creates User + Merchant + Store atomically."""
        from app.modules.billing.models import SubscriptionTier
        from app.modules.tenancy.models import Platform

        # Create platform
        platform = Platform(
            code=f"test_{uuid.uuid4().hex[:8]}",
            name="Test Platform",
            is_active=True,
        )
        db.add(platform)
        db.commit()

        # Create a tier for the platform
        tier = SubscriptionTier(
            code=TierCode.ESSENTIAL.value,
            name="Essential",
            platform_id=platform.id,
            price_monthly_cents=0,
            display_order=1,
            is_active=True,
            is_public=True,
        )
        db.add(tier)
        db.commit()

        # Create session
        session_id = self.service.create_session(
            tier_code=TierCode.ESSENTIAL.value,
            is_annual=False,
            platform_code=platform.code,
            language="de",
        )

        # Mock Stripe
        with patch(
            "app.modules.billing.services.signup_service.stripe_service"
        ) as mock_stripe:
            mock_stripe.create_customer_for_merchant.return_value = "cus_test123"

            result = self.service.create_account(
                db=db,
                session_id=session_id,
                email=f"test_{uuid.uuid4().hex[:8]}@example.com",
                password="securepass123",  # noqa: SEC-001  # noqa: SEC-001
                first_name="John",
                last_name="Doe",
                merchant_name="John's Shop",
            )

        # Verify result includes store info
        assert result.user_id is not None
        assert result.merchant_id is not None
        assert result.store_id is not None
        assert result.store_code is not None
        assert result.stripe_customer_id == "cus_test123"

        # Verify store was created with correct language
        from app.modules.tenancy.models import Store

        store = db.query(Store).filter(Store.id == result.store_id).first()
        assert store is not None
        assert store.name == "John's Shop"
        assert store.default_language == "de"

    def test_create_account_uses_session_language(self, db):
        """create_account sets store default_language from the signup session."""
        from app.modules.billing.models import SubscriptionTier
        from app.modules.tenancy.models import Platform

        platform = Platform(
            code=f"test_{uuid.uuid4().hex[:8]}",
            name="Test Platform",
            is_active=True,
        )
        db.add(platform)
        db.commit()

        tier = SubscriptionTier(
            code=TierCode.ESSENTIAL.value,
            name="Essential",
            platform_id=platform.id,
            price_monthly_cents=0,
            display_order=1,
            is_active=True,
            is_public=True,
        )
        db.add(tier)
        db.commit()

        session_id = self.service.create_session(
            tier_code=TierCode.ESSENTIAL.value,
            is_annual=False,
            platform_code=platform.code,
            language="en",
        )

        with patch(
            "app.modules.billing.services.signup_service.stripe_service"
        ) as mock_stripe:
            mock_stripe.create_customer_for_merchant.return_value = "cus_test456"

            result = self.service.create_account(
                db=db,
                session_id=session_id,
                email=f"test_{uuid.uuid4().hex[:8]}@example.com",
                password="securepass123",  # noqa: SEC-001
                first_name="Jane",
                last_name="Smith",
                merchant_name="Jane's Bakery",
            )

        from app.modules.tenancy.models import Store

        store = db.query(Store).filter(Store.id == result.store_id).first()
        assert store.default_language == "en"

    def test_create_account_rejects_duplicate_email(self, db):
        """create_account raises ConflictException for existing email."""
        from app.modules.billing.models import SubscriptionTier
        from app.modules.tenancy.models import Platform

        platform = Platform(
            code=f"test_{uuid.uuid4().hex[:8]}",
            name="Test Platform",
            is_active=True,
        )
        db.add(platform)
        db.commit()

        tier = SubscriptionTier(
            code=TierCode.ESSENTIAL.value,
            name="Essential",
            platform_id=platform.id,
            price_monthly_cents=0,
            display_order=1,
            is_active=True,
            is_public=True,
        )
        db.add(tier)
        db.commit()

        email = f"dup_{uuid.uuid4().hex[:8]}@example.com"

        # Create first account
        session_id1 = self.service.create_session(
            tier_code=TierCode.ESSENTIAL.value,
            is_annual=False,
            platform_code=platform.code,
        )

        with patch(
            "app.modules.billing.services.signup_service.stripe_service"
        ) as mock_stripe:
            mock_stripe.create_customer_for_merchant.return_value = "cus_first"
            self.service.create_account(
                db=db,
                session_id=session_id1,
                email=email,
                password="securepass123",  # noqa: SEC-001
                first_name="First",
                last_name="User",
                merchant_name="First Shop",
            )

        # Try to create second account with same email
        session_id2 = self.service.create_session(
            tier_code=TierCode.ESSENTIAL.value,
            is_annual=False,
            platform_code=platform.code,
        )

        with pytest.raises(ConflictException):
            self.service.create_account(
                db=db,
                session_id=session_id2,
                email=email,
                password="securepass123",  # noqa: SEC-001
                first_name="Second",
                last_name="User",
                merchant_name="Second Shop",
            )

    def test_create_account_updates_session(self, db):
        """create_account updates session with user/merchant/store IDs."""
        from app.modules.billing.models import SubscriptionTier
        from app.modules.tenancy.models import Platform

        platform = Platform(
            code=f"test_{uuid.uuid4().hex[:8]}",
            name="Test Platform",
            is_active=True,
        )
        db.add(platform)
        db.commit()

        tier = SubscriptionTier(
            code=TierCode.ESSENTIAL.value,
            name="Essential",
            platform_id=platform.id,
            price_monthly_cents=0,
            display_order=1,
            is_active=True,
            is_public=True,
        )
        db.add(tier)
        db.commit()

        session_id = self.service.create_session(
            tier_code=TierCode.ESSENTIAL.value,
            is_annual=False,
            platform_code=platform.code,
        )

        with patch(
            "app.modules.billing.services.signup_service.stripe_service"
        ) as mock_stripe:
            mock_stripe.create_customer_for_merchant.return_value = "cus_test789"

            result = self.service.create_account(
                db=db,
                session_id=session_id,
                email=f"test_{uuid.uuid4().hex[:8]}@example.com",
                password="securepass123",  # noqa: SEC-001
                first_name="Test",
                last_name="User",
                merchant_name="Test Shop",
            )

        session = self.service.get_session(session_id)
        assert session["user_id"] == result.user_id
        assert session["merchant_id"] == result.merchant_id
        assert session["store_id"] == result.store_id
        assert session["store_code"] == result.store_code
        assert session["stripe_customer_id"] == "cus_test789"
        assert session["step"] == "account_created"


@pytest.mark.unit
@pytest.mark.billing
class TestSignupServicePostRedirect:
    """Tests for SignupService._get_post_signup_redirect."""

    def setup_method(self):
        self.service = SignupService()

    def test_always_redirects_to_dashboard(self, db):
        """Post-signup redirect always goes to store dashboard."""
        session = {"platform_code": "loyalty"}
        url = self.service._get_post_signup_redirect(db, session, "MY_STORE")
        assert url == "/store/MY_STORE/dashboard"

    def test_redirect_for_oms_platform(self, db):
        """OMS platform also redirects to dashboard (not onboarding wizard)."""
        session = {"platform_code": "oms"}
        url = self.service._get_post_signup_redirect(db, session, "OMS_STORE")
        assert url == "/store/OMS_STORE/dashboard"
