# tests/unit/services/test_store_subdomain_service.py
"""Unit tests for StoreSubdomainService."""

import uuid

import pytest

from app.exceptions.base import (
    ConflictException,
    ResourceNotFoundException,
    ValidationException,
)
from app.modules.tenancy.models import Platform, Store
from app.modules.tenancy.models.store_platform import StorePlatform
from app.modules.tenancy.services.store_subdomain_service import StoreSubdomainService


@pytest.fixture
def subdomain_service():
    return StoreSubdomainService()


@pytest.fixture
def sd_merchant(db, test_admin):
    """Create a merchant for subdomain tests."""
    from app.modules.tenancy.models import Merchant

    unique_id = str(uuid.uuid4())[:8]
    merchant = Merchant(
        name=f"SD Merchant {unique_id}",
        owner_user_id=test_admin.id,
        contact_email=f"sd{unique_id}@test.com",
        is_active=True,
        is_verified=True,
    )
    db.add(merchant)
    db.commit()
    db.refresh(merchant)
    return merchant


@pytest.fixture
def sd_platform(db):
    """Create a test platform."""
    unique_id = str(uuid.uuid4())[:8]
    platform = Platform(
        code=f"sd{unique_id}",
        name=f"SD Platform {unique_id}",
        domain=f"sd{unique_id}.example.com",
        is_active=True,
    )
    db.add(platform)
    db.commit()
    db.refresh(platform)
    return platform


@pytest.fixture
def sd_platform_2(db):
    """Create a second test platform."""
    unique_id = str(uuid.uuid4())[:8]
    platform = Platform(
        code=f"sd2{unique_id}",
        name=f"SD Platform 2 {unique_id}",
        domain=f"sd2{unique_id}.example.com",
        is_active=True,
    )
    db.add(platform)
    db.commit()
    db.refresh(platform)
    return platform


@pytest.fixture
def sd_store(db, sd_merchant):
    """Create a test store."""
    unique_id = str(uuid.uuid4())[:8]
    store = Store(
        store_code=f"SD{unique_id}".upper(),
        name=f"SD Store {unique_id}",
        subdomain=f"sdstore{unique_id}",
        merchant_id=sd_merchant.id,
        is_active=True,
    )
    db.add(store)
    db.commit()
    db.refresh(store)
    return store


@pytest.fixture
def sd_membership(db, sd_store, sd_platform):
    """Create a StorePlatform membership."""
    sp = StorePlatform(
        store_id=sd_store.id,
        platform_id=sd_platform.id,
        is_active=True,
    )
    db.add(sp)
    db.commit()
    db.refresh(sp)
    return sp


@pytest.mark.unit
@pytest.mark.stores
class TestGetCustomSubdomains:
    """Tests for listing custom subdomains."""

    def test_list_returns_memberships(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        results = subdomain_service.get_custom_subdomains(db, sd_store.id)

        assert len(results) == 1
        assert results[0]["platform_code"] == sd_platform.code
        assert results[0]["custom_subdomain"] is None
        assert results[0]["default_subdomain"] == sd_store.subdomain

    def test_list_shows_custom_subdomain(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        sd_membership.custom_subdomain = "my-custom"
        db.commit()

        results = subdomain_service.get_custom_subdomains(db, sd_store.id)

        assert results[0]["custom_subdomain"] == "my-custom"
        assert results[0]["full_url"] == f"my-custom.{sd_platform.domain}"

    def test_list_nonexistent_store_raises(self, db, subdomain_service):
        with pytest.raises(ResourceNotFoundException):
            subdomain_service.get_custom_subdomains(db, 999999)

    def test_list_excludes_inactive_memberships(self, db, subdomain_service, sd_store, sd_membership):
        sd_membership.is_active = False
        db.commit()

        results = subdomain_service.get_custom_subdomains(db, sd_store.id)

        assert len(results) == 0


@pytest.mark.unit
@pytest.mark.stores
class TestSetCustomSubdomain:
    """Tests for setting a custom subdomain."""

    def test_set_valid_subdomain(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        sp = subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "my-store")

        assert sp.custom_subdomain == "my-store"

    def test_set_normalizes_to_lowercase(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        sp = subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "My-Store")

        assert sp.custom_subdomain == "my-store"

    def test_set_strips_whitespace(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        sp = subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "  my-store  ")

        assert sp.custom_subdomain == "my-store"

    def test_set_rejects_leading_hyphen(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        with pytest.raises(ValidationException):
            subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "-invalid")

    def test_set_rejects_trailing_hyphen(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        with pytest.raises(ValidationException):
            subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "invalid-")

    def test_set_rejects_uppercase_special_chars(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        with pytest.raises(ValidationException):
            subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "inv@lid")

    def test_set_rejects_too_short(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        with pytest.raises(ValidationException):
            subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "ab")

    def test_set_nonexistent_membership_raises(self, db, subdomain_service, sd_store):
        with pytest.raises(ResourceNotFoundException):
            subdomain_service.set_custom_subdomain(db, sd_store.id, 999999, "test-sub")

    def test_set_duplicate_on_same_platform_raises(
        self, db, subdomain_service, sd_store, sd_membership, sd_platform, sd_merchant
    ):
        # Create another store with same subdomain on same platform
        unique_id = str(uuid.uuid4())[:8]
        other_store = Store(
            store_code=f"OTHER{unique_id}".upper(),
            name=f"Other Store {unique_id}",
            subdomain=f"other{unique_id}",
            merchant_id=sd_merchant.id,
            is_active=True,
        )
        db.add(other_store)
        db.flush()

        other_sp = StorePlatform(
            store_id=other_store.id,
            platform_id=sd_platform.id,
            is_active=True,
            custom_subdomain="taken-name",
        )
        db.add(other_sp)
        db.commit()

        with pytest.raises(ConflictException):
            subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "taken-name")

    def test_set_same_subdomain_different_platform_ok(
        self, db, subdomain_service, sd_store, sd_membership, sd_platform, sd_platform_2
    ):
        # Set on first platform
        subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "shared-name")
        db.commit()

        # Create membership on second platform
        sp2 = StorePlatform(
            store_id=sd_store.id,
            platform_id=sd_platform_2.id,
            is_active=True,
        )
        db.add(sp2)
        db.commit()

        # Same subdomain on different platform should work
        result = subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform_2.id, "shared-name")

        assert result.custom_subdomain == "shared-name"

    def test_update_existing_subdomain(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "first")
        db.commit()

        sp = subdomain_service.set_custom_subdomain(db, sd_store.id, sd_platform.id, "second")

        assert sp.custom_subdomain == "second"


@pytest.mark.unit
@pytest.mark.stores
class TestClearCustomSubdomain:
    """Tests for clearing a custom subdomain."""

    def test_clear_existing_subdomain(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        sd_membership.custom_subdomain = "to-clear"
        db.commit()

        sp = subdomain_service.clear_custom_subdomain(db, sd_store.id, sd_platform.id)

        assert sp.custom_subdomain is None

    def test_clear_already_none_ok(self, db, subdomain_service, sd_store, sd_membership, sd_platform):
        sp = subdomain_service.clear_custom_subdomain(db, sd_store.id, sd_platform.id)

        assert sp.custom_subdomain is None

    def test_clear_nonexistent_membership_raises(self, db, subdomain_service, sd_store):
        with pytest.raises(ResourceNotFoundException):
            subdomain_service.clear_custom_subdomain(db, sd_store.id, 999999)
