# tests/integration/api/v1/platform/test_signup.py
"""Integration tests for platform signup API endpoints.

Tests the /api/v1/platform/signup/* endpoints.
"""

from unittest.mock import MagicMock, patch

import pytest

from app.modules.tenancy.models import Company
from app.modules.billing.models import TierCode
from app.modules.tenancy.models import User
from app.modules.tenancy.models import Vendor


@pytest.fixture
def mock_stripe_service():
    """Mock the Stripe service for tests."""
    with patch("app.modules.marketplace.services.platform_signup_service.stripe_service") as mock:
        mock.create_customer.return_value = "cus_test_123"
        mock.create_setup_intent.return_value = MagicMock(
            id="seti_test_123",
            client_secret="seti_test_123_secret_abc",
            status="requires_payment_method",
        )
        mock.get_setup_intent.return_value = MagicMock(
            id="seti_test_123",
            status="succeeded",
            payment_method="pm_test_123",
        )
        mock.attach_payment_method_to_customer.return_value = None
        yield mock


@pytest.fixture
def signup_session(client):
    """Create a signup session for testing."""
    response = client.post(
        "/api/v1/platform/signup/start",
        json={"tier_code": TierCode.PROFESSIONAL.value, "is_annual": False},
    )
    return response.json()["session_id"]


@pytest.fixture
def existing_user(db, auth_manager):
    """Create an existing user for testing duplicate email."""
    user = User(
        email="existing@example.com",
        username="existing_user",
        hashed_password=auth_manager.hash_password("password123"),
        first_name="Existing",
        last_name="User",
        role="vendor",
        is_active=True,
    )
    db.add(user)
    db.commit()
    return user


@pytest.fixture
def claimed_owner_user(db, auth_manager):
    """Create an owner user for the claimed vendor."""
    user = User(
        email="claimed_owner@test.com",
        username="claimed_owner",
        hashed_password=auth_manager.hash_password("testpass123"),
        role="vendor",
        is_active=True,
    )
    db.add(user)
    db.commit()
    return user


@pytest.fixture
def claimed_letzshop_vendor(db, claimed_owner_user):
    """Create a vendor that has already claimed a Letzshop shop."""
    company = Company(
        name="Claimed Company",
        owner_user_id=claimed_owner_user.id,
        contact_email="claimed@test.com",
    )
    db.add(company)
    db.flush()

    vendor = Vendor(
        company_id=company.id,
        vendor_code="CLAIMED",
        subdomain="claimed",
        name="Claimed Vendor",
        contact_email="claimed@test.com",
        is_active=True,
        letzshop_vendor_slug="already-claimed-shop",
    )
    db.add(vendor)
    db.commit()
    return vendor


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.platform
class TestSignupStartAPI:
    """Test signup start endpoint at /api/v1/platform/signup/start."""

    def test_start_signup_success(self, client):
        """Test starting a signup session."""
        response = client.post(
            "/api/v1/platform/signup/start",
            json={"tier_code": TierCode.ESSENTIAL.value, "is_annual": False},
        )

        assert response.status_code == 200
        data = response.json()
        assert "session_id" in data
        assert data["tier_code"] == TierCode.ESSENTIAL.value
        assert data["is_annual"] is False

    def test_start_signup_with_annual_billing(self, client):
        """Test starting signup with annual billing."""
        response = client.post(
            "/api/v1/platform/signup/start",
            json={"tier_code": TierCode.PROFESSIONAL.value, "is_annual": True},
        )

        assert response.status_code == 200
        data = response.json()
        assert data["is_annual"] is True

    def test_start_signup_all_tiers(self, client):
        """Test starting signup for all valid tiers."""
        for tier in [TierCode.ESSENTIAL, TierCode.PROFESSIONAL, TierCode.BUSINESS, TierCode.ENTERPRISE]:
            response = client.post(
                "/api/v1/platform/signup/start",
                json={"tier_code": tier.value, "is_annual": False},
            )
            assert response.status_code == 200
            assert response.json()["tier_code"] == tier.value

    def test_start_signup_invalid_tier(self, client):
        """Test starting signup with invalid tier code."""
        response = client.post(
            "/api/v1/platform/signup/start",
            json={"tier_code": "invalid_tier", "is_annual": False},
        )

        assert response.status_code == 422  # ValidationException
        data = response.json()
        assert "invalid tier" in data["message"].lower()

    def test_start_signup_session_id_is_unique(self, client):
        """Test that each signup session gets a unique ID."""
        response1 = client.post(
            "/api/v1/platform/signup/start",
            json={"tier_code": TierCode.ESSENTIAL.value, "is_annual": False},
        )
        response2 = client.post(
            "/api/v1/platform/signup/start",
            json={"tier_code": TierCode.ESSENTIAL.value, "is_annual": False},
        )

        assert response1.json()["session_id"] != response2.json()["session_id"]


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.platform
class TestClaimVendorAPI:
    """Test claim vendor endpoint at /api/v1/platform/signup/claim-vendor."""

    def test_claim_vendor_success(self, client, signup_session):
        """Test claiming a Letzshop vendor."""
        response = client.post(
            "/api/v1/platform/signup/claim-vendor",
            json={
                "session_id": signup_session,
                "letzshop_slug": "my-new-shop",
            },
        )

        assert response.status_code == 200
        data = response.json()
        assert data["session_id"] == signup_session
        assert data["letzshop_slug"] == "my-new-shop"
        assert data["vendor_name"] is not None

    def test_claim_vendor_with_vendor_id(self, client, signup_session):
        """Test claiming vendor with Letzshop vendor ID."""
        response = client.post(
            "/api/v1/platform/signup/claim-vendor",
            json={
                "session_id": signup_session,
                "letzshop_slug": "my-shop",
                "letzshop_vendor_id": "letz_vendor_123",
            },
        )

        assert response.status_code == 200
        data = response.json()
        assert data["letzshop_slug"] == "my-shop"

    def test_claim_vendor_invalid_session(self, client):
        """Test claiming vendor with invalid session."""
        response = client.post(
            "/api/v1/platform/signup/claim-vendor",
            json={
                "session_id": "invalid_session_id",
                "letzshop_slug": "my-shop",
            },
        )

        assert response.status_code == 404
        data = response.json()
        assert "not found" in data["message"].lower()

    def test_claim_vendor_already_claimed(self, client, signup_session, claimed_letzshop_vendor):
        """Test claiming a vendor that's already claimed."""
        response = client.post(
            "/api/v1/platform/signup/claim-vendor",
            json={
                "session_id": signup_session,
                "letzshop_slug": "already-claimed-shop",
            },
        )

        assert response.status_code == 409  # ConflictException
        data = response.json()
        assert "already claimed" in data["message"].lower()


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.platform
class TestCreateAccountAPI:
    """Test create account endpoint at /api/v1/platform/signup/create-account."""

    def test_create_account_success(self, client, signup_session, mock_stripe_service):
        """Test creating an account."""
        response = client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": signup_session,
                "email": "newuser@example.com",
                "password": "SecurePass123!",
                "first_name": "John",
                "last_name": "Doe",
                "company_name": "Test Company",
            },
        )

        assert response.status_code == 200
        data = response.json()
        assert data["session_id"] == signup_session
        assert data["user_id"] > 0
        assert data["vendor_id"] > 0
        assert data["stripe_customer_id"] == "cus_test_123"

    def test_create_account_with_phone(self, client, signup_session, mock_stripe_service):
        """Test creating an account with phone number."""
        response = client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": signup_session,
                "email": "user2@example.com",
                "password": "SecurePass123!",
                "first_name": "Jane",
                "last_name": "Smith",
                "company_name": "Another Company",
                "phone": "+352 123 456 789",
            },
        )

        assert response.status_code == 200
        data = response.json()
        assert data["user_id"] > 0

    def test_create_account_invalid_session(self, client, mock_stripe_service):
        """Test creating account with invalid session."""
        response = client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": "invalid_session",
                "email": "test@example.com",
                "password": "SecurePass123!",
                "first_name": "Test",
                "last_name": "User",
                "company_name": "Test Co",
            },
        )

        assert response.status_code == 404

    def test_create_account_duplicate_email(
        self, client, signup_session, existing_user, mock_stripe_service
    ):
        """Test creating account with existing email."""
        response = client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": signup_session,
                "email": "existing@example.com",
                "password": "SecurePass123!",
                "first_name": "Another",
                "last_name": "User",
                "company_name": "Duplicate Co",
            },
        )

        assert response.status_code == 409  # ConflictException
        data = response.json()
        assert "already exists" in data["message"].lower()

    def test_create_account_invalid_email(self, client, signup_session):
        """Test creating account with invalid email format."""
        response = client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": signup_session,
                "email": "not-an-email",
                "password": "SecurePass123!",
                "first_name": "Test",
                "last_name": "User",
                "company_name": "Test Co",
            },
        )

        assert response.status_code == 422  # Validation error

    def test_create_account_with_letzshop_claim(self, client, mock_stripe_service):
        """Test creating account after claiming Letzshop vendor."""
        # Start signup
        start_response = client.post(
            "/api/v1/platform/signup/start",
            json={"tier_code": TierCode.PROFESSIONAL.value, "is_annual": False},
        )
        session_id = start_response.json()["session_id"]

        # Claim vendor
        client.post(
            "/api/v1/platform/signup/claim-vendor",
            json={
                "session_id": session_id,
                "letzshop_slug": "my-shop-claim",
            },
        )

        # Create account
        response = client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": session_id,
                "email": "shop@example.com",
                "password": "SecurePass123!",
                "first_name": "Shop",
                "last_name": "Owner",
                "company_name": "My Shop",
            },
        )

        assert response.status_code == 200
        data = response.json()
        assert data["vendor_id"] > 0


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.platform
class TestSetupPaymentAPI:
    """Test setup payment endpoint at /api/v1/platform/signup/setup-payment."""

    def test_setup_payment_success(self, client, signup_session, mock_stripe_service):
        """Test setting up payment after account creation."""
        # Create account first
        client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": signup_session,
                "email": "payment@example.com",
                "password": "SecurePass123!",
                "first_name": "Payment",
                "last_name": "Test",
                "company_name": "Payment Co",
            },
        )

        # Setup payment
        response = client.post(
            "/api/v1/platform/signup/setup-payment",
            json={"session_id": signup_session},
        )

        assert response.status_code == 200
        data = response.json()
        assert data["session_id"] == signup_session
        assert "client_secret" in data
        assert data["stripe_customer_id"] == "cus_test_123"

    def test_setup_payment_invalid_session(self, client, mock_stripe_service):
        """Test setup payment with invalid session."""
        response = client.post(
            "/api/v1/platform/signup/setup-payment",
            json={"session_id": "invalid_session"},
        )

        assert response.status_code == 404

    def test_setup_payment_without_account(self, client, signup_session, mock_stripe_service):
        """Test setup payment without creating account first."""
        response = client.post(
            "/api/v1/platform/signup/setup-payment",
            json={"session_id": signup_session},
        )

        assert response.status_code == 422  # ValidationException
        data = response.json()
        assert "account not created" in data["message"].lower()


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.platform
class TestCompleteSignupAPI:
    """Test complete signup endpoint at /api/v1/platform/signup/complete."""

    def test_complete_signup_success(self, client, signup_session, mock_stripe_service, db):
        """Test completing signup after payment setup."""
        # Create account
        client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": signup_session,
                "email": "complete@example.com",
                "password": "SecurePass123!",
                "first_name": "Complete",
                "last_name": "User",
                "company_name": "Complete Co",
            },
        )

        # Setup payment
        client.post(
            "/api/v1/platform/signup/setup-payment",
            json={"session_id": signup_session},
        )

        # Complete signup
        response = client.post(
            "/api/v1/platform/signup/complete",
            json={
                "session_id": signup_session,
                "setup_intent_id": "seti_test_123",
            },
        )

        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True
        assert "vendor_code" in data
        assert "vendor_id" in data
        assert "redirect_url" in data
        assert "trial_ends_at" in data

    def test_complete_signup_returns_access_token(
        self, client, signup_session, mock_stripe_service, db
    ):
        """Test that completing signup returns a valid JWT access token for auto-login."""
        # Create account
        client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": signup_session,
                "email": "token_test@example.com",
                "password": "SecurePass123!",
                "first_name": "Token",
                "last_name": "Test",
                "company_name": "Token Test Co",
            },
        )

        # Setup payment
        client.post(
            "/api/v1/platform/signup/setup-payment",
            json={"session_id": signup_session},
        )

        # Complete signup
        response = client.post(
            "/api/v1/platform/signup/complete",
            json={
                "session_id": signup_session,
                "setup_intent_id": "seti_test_123",
            },
        )

        assert response.status_code == 200
        data = response.json()

        # Verify access_token is returned
        assert "access_token" in data
        assert data["access_token"] is not None
        assert len(data["access_token"]) > 50  # JWT tokens are long

    def test_complete_signup_token_can_authenticate(
        self, client, signup_session, mock_stripe_service, db
    ):
        """Test that the returned access token can be used to authenticate API calls."""
        # Create account
        client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": signup_session,
                "email": "auth_test@example.com",
                "password": "SecurePass123!",
                "first_name": "Auth",
                "last_name": "Test",
                "company_name": "Auth Test Co",
            },
        )

        # Setup payment
        client.post(
            "/api/v1/platform/signup/setup-payment",
            json={"session_id": signup_session},
        )

        # Complete signup
        complete_response = client.post(
            "/api/v1/platform/signup/complete",
            json={
                "session_id": signup_session,
                "setup_intent_id": "seti_test_123",
            },
        )

        assert complete_response.status_code == 200
        access_token = complete_response.json()["access_token"]

        # Use the token to access a protected vendor endpoint
        auth_response = client.get(
            "/api/v1/vendor/onboarding/status",
            headers={"Authorization": f"Bearer {access_token}"},
        )

        # Should be able to access the onboarding endpoint
        assert auth_response.status_code == 200

    def test_complete_signup_sets_vendor_token_cookie(
        self, client, signup_session, mock_stripe_service, db
    ):
        """Test that completing signup sets the vendor_token HTTP-only cookie."""
        # Create account
        client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": signup_session,
                "email": "cookie_test@example.com",
                "password": "SecurePass123!",
                "first_name": "Cookie",
                "last_name": "Test",
                "company_name": "Cookie Test Co",
            },
        )

        # Setup payment
        client.post(
            "/api/v1/platform/signup/setup-payment",
            json={"session_id": signup_session},
        )

        # Complete signup
        response = client.post(
            "/api/v1/platform/signup/complete",
            json={
                "session_id": signup_session,
                "setup_intent_id": "seti_test_123",
            },
        )

        assert response.status_code == 200

        # Check that vendor_token cookie is set
        cookies = response.cookies
        assert "vendor_token" in cookies

    def test_complete_signup_invalid_session(self, client, mock_stripe_service):
        """Test completing signup with invalid session."""
        response = client.post(
            "/api/v1/platform/signup/complete",
            json={
                "session_id": "invalid_session",
                "setup_intent_id": "seti_test_123",
            },
        )

        assert response.status_code == 404

    def test_complete_signup_payment_not_succeeded(
        self, client, signup_session, mock_stripe_service
    ):
        """Test completing signup when payment setup failed."""
        # Create account
        client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": signup_session,
                "email": "fail@example.com",
                "password": "SecurePass123!",
                "first_name": "Fail",
                "last_name": "User",
                "company_name": "Fail Co",
            },
        )

        # Setup payment
        client.post(
            "/api/v1/platform/signup/setup-payment",
            json={"session_id": signup_session},
        )

        # Mock failed setup intent
        mock_stripe_service.get_setup_intent.return_value = MagicMock(
            id="seti_failed",
            status="requires_payment_method",
            payment_method=None,
        )

        # Complete signup
        response = client.post(
            "/api/v1/platform/signup/complete",
            json={
                "session_id": signup_session,
                "setup_intent_id": "seti_failed",
            },
        )

        assert response.status_code == 422  # ValidationException
        data = response.json()
        assert "not completed" in data["message"].lower()


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.platform
class TestGetSignupSessionAPI:
    """Test get signup session endpoint at /api/v1/platform/signup/session/{session_id}."""

    def test_get_session_after_start(self, client, signup_session):
        """Test getting session after starting signup."""
        response = client.get(f"/api/v1/platform/signup/session/{signup_session}")

        assert response.status_code == 200
        data = response.json()
        assert data["session_id"] == signup_session
        assert data["step"] == "tier_selected"
        assert data["tier_code"] == TierCode.PROFESSIONAL.value
        assert "created_at" in data

    def test_get_session_after_claim(self, client, signup_session):
        """Test getting session after claiming vendor."""
        client.post(
            "/api/v1/platform/signup/claim-vendor",
            json={
                "session_id": signup_session,
                "letzshop_slug": "my-session-shop",
            },
        )

        response = client.get(f"/api/v1/platform/signup/session/{signup_session}")

        assert response.status_code == 200
        data = response.json()
        assert data["step"] == "vendor_claimed"
        assert data["letzshop_slug"] == "my-session-shop"

    def test_get_session_invalid_id(self, client):
        """Test getting non-existent session."""
        response = client.get("/api/v1/platform/signup/session/invalid_id")

        assert response.status_code == 404


@pytest.mark.integration
@pytest.mark.api
@pytest.mark.platform
class TestSignupFullFlow:
    """Test complete signup flow end-to-end."""

    def test_full_signup_flow(self, client, mock_stripe_service, db):
        """Test the complete signup flow from start to finish."""
        # Step 1: Start signup
        start_response = client.post(
            "/api/v1/platform/signup/start",
            json={"tier_code": TierCode.BUSINESS.value, "is_annual": True},
        )
        assert start_response.status_code == 200
        session_id = start_response.json()["session_id"]

        # Step 2: Claim Letzshop vendor (optional)
        claim_response = client.post(
            "/api/v1/platform/signup/claim-vendor",
            json={
                "session_id": session_id,
                "letzshop_slug": "full-flow-shop",
            },
        )
        assert claim_response.status_code == 200

        # Step 3: Create account
        account_response = client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": session_id,
                "email": "fullflow@example.com",
                "password": "SecurePass123!",
                "first_name": "Full",
                "last_name": "Flow",
                "company_name": "Full Flow Company",
                "phone": "+352 123 456",
            },
        )
        assert account_response.status_code == 200
        vendor_id = account_response.json()["vendor_id"]

        # Step 4: Setup payment
        payment_response = client.post(
            "/api/v1/platform/signup/setup-payment",
            json={"session_id": session_id},
        )
        assert payment_response.status_code == 200
        assert "client_secret" in payment_response.json()

        # Step 5: Complete signup
        complete_response = client.post(
            "/api/v1/platform/signup/complete",
            json={
                "session_id": session_id,
                "setup_intent_id": "seti_test_123",
            },
        )
        assert complete_response.status_code == 200
        assert complete_response.json()["success"] is True

        # Verify vendor was created with Letzshop link
        vendor = db.query(Vendor).filter(Vendor.id == vendor_id).first()
        assert vendor is not None
        assert vendor.letzshop_vendor_slug == "full-flow-shop"
        assert vendor.is_active is True

    def test_signup_flow_without_letzshop_claim(self, client, mock_stripe_service, db):
        """Test signup flow skipping Letzshop claim step."""
        # Step 1: Start signup
        start_response = client.post(
            "/api/v1/platform/signup/start",
            json={"tier_code": TierCode.ESSENTIAL.value, "is_annual": False},
        )
        session_id = start_response.json()["session_id"]

        # Skip Step 2, go directly to Step 3
        account_response = client.post(
            "/api/v1/platform/signup/create-account",
            json={
                "session_id": session_id,
                "email": "noletzshop@example.com",
                "password": "SecurePass123!",
                "first_name": "No",
                "last_name": "Letzshop",
                "company_name": "Independent Shop",
            },
        )
        assert account_response.status_code == 200
        vendor_id = account_response.json()["vendor_id"]

        # Step 4 & 5: Payment and complete
        client.post(
            "/api/v1/platform/signup/setup-payment",
            json={"session_id": session_id},
        )

        complete_response = client.post(
            "/api/v1/platform/signup/complete",
            json={
                "session_id": session_id,
                "setup_intent_id": "seti_test_123",
            },
        )
        assert complete_response.status_code == 200

        # Verify vendor was created without Letzshop link
        vendor = db.query(Vendor).filter(Vendor.id == vendor_id).first()
        assert vendor is not None
        assert vendor.letzshop_vendor_slug is None
