# tests/unit/test_merchant_store_service.py
"""Unit tests for MerchantStoreService."""

import uuid
from unittest.mock import patch

import pytest

from app.modules.tenancy.exceptions import (
    MerchantNotFoundException,
    StoreAlreadyExistsException,
    StoreNotFoundException,
    StoreValidationException,
)
from app.modules.tenancy.models import Merchant, Store
from app.modules.tenancy.services.merchant_store_service import MerchantStoreService


@pytest.fixture
def merchant_owner(db, test_user):
    """Create a merchant owned by test_user."""
    unique_id = str(uuid.uuid4())[:8]
    merchant = Merchant(
        name=f"Test Merchant {unique_id}",
        owner_user_id=test_user.id,
        contact_email=f"merchant{unique_id}@test.com",
        is_active=True,
        is_verified=True,
    )
    db.add(merchant)
    db.commit()
    db.refresh(merchant)
    return merchant


@pytest.fixture
def other_merchant_owner(db):
    """Create a separate merchant for ownership isolation tests."""
    from app.modules.tenancy.models.user import User

    unique_id = str(uuid.uuid4())[:8]
    user = User(
        email=f"other{unique_id}@test.com",
        username=f"other_{unique_id}",
        hashed_password="fakehash",  # noqa: SEC001
        role="merchant_owner",
        is_active=True,
    )
    db.add(user)
    db.flush()

    merchant = Merchant(
        name=f"Other Merchant {unique_id}",
        owner_user_id=user.id,
        contact_email=f"otherm{unique_id}@test.com",
        is_active=True,
        is_verified=True,
    )
    db.add(merchant)
    db.commit()
    db.refresh(merchant)
    return merchant


@pytest.fixture
def merchant_store(db, merchant_owner):
    """Create a store under the merchant."""
    unique_id = str(uuid.uuid4())[:8]
    store = Store(
        merchant_id=merchant_owner.id,
        store_code=f"MS_{unique_id}".upper(),
        subdomain=f"ms-{unique_id}".lower(),
        name=f"Merchant Store {unique_id}",
        description="A test store",
        is_active=True,
        is_verified=False,
    )
    db.add(store)
    db.commit()
    db.refresh(store)
    return store


@pytest.mark.unit
@pytest.mark.tenancy
class TestMerchantStoreServiceGetDetail:
    """Tests for get_store_detail."""

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

    def test_get_store_detail_success(self, db, merchant_owner, merchant_store):
        """Test getting store detail with valid ownership."""
        result = self.service.get_store_detail(db, merchant_owner.id, merchant_store.id)

        assert result["id"] == merchant_store.id
        assert result["store_code"] == merchant_store.store_code
        assert result["name"] == merchant_store.name
        assert result["is_active"] is True
        assert "platforms" in result

    def test_get_store_detail_wrong_merchant(
        self, db, other_merchant_owner, merchant_store
    ):
        """Test that accessing another merchant's store raises not found."""
        with pytest.raises(StoreNotFoundException):
            self.service.get_store_detail(
                db, other_merchant_owner.id, merchant_store.id
            )

    def test_get_store_detail_nonexistent(self, db, merchant_owner):
        """Test getting a non-existent store."""
        with pytest.raises(StoreNotFoundException):
            self.service.get_store_detail(db, merchant_owner.id, 99999)


@pytest.mark.unit
@pytest.mark.tenancy
class TestMerchantStoreServiceUpdate:
    """Tests for update_store."""

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

    def test_update_store_success(self, db, merchant_owner, merchant_store):
        """Test updating store fields."""
        result = self.service.update_store(
            db,
            merchant_owner.id,
            merchant_store.id,
            {"name": "Updated Name", "contact_email": "new@test.com"},
        )
        db.commit()

        assert result["name"] == "Updated Name"
        assert result["contact_email"] == "new@test.com"

    def test_update_store_ignores_disallowed_fields(
        self, db, merchant_owner, merchant_store
    ):
        """Test that admin-only fields are ignored."""
        result = self.service.update_store(
            db,
            merchant_owner.id,
            merchant_store.id,
            {"is_active": False, "is_verified": True, "name": "OK Name"},
        )
        db.commit()

        # is_active and is_verified should NOT change
        assert result["is_active"] is True
        assert result["is_verified"] is False
        assert result["name"] == "OK Name"

    def test_update_store_wrong_merchant(
        self, db, other_merchant_owner, merchant_store
    ):
        """Test that updating another merchant's store raises not found."""
        with pytest.raises(StoreNotFoundException):
            self.service.update_store(
                db,
                other_merchant_owner.id,
                merchant_store.id,
                {"name": "Hacked"},
            )


@pytest.mark.unit
@pytest.mark.tenancy
class TestMerchantStoreServiceCreate:
    """Tests for create_store."""

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

    @patch.object(MerchantStoreService, "can_create_store", return_value=(True, None))
    def test_create_store_success(self, _mock_limit, db, merchant_owner):
        """Test successful store creation."""
        unique_id = str(uuid.uuid4())[:8]
        store_data = {
            "name": f"New Store {unique_id}",
            "store_code": f"NEW_{unique_id}",
            "subdomain": f"new-{unique_id}".lower(),
            "description": "Test description",
            "platform_ids": [],
        }

        result = self.service.create_store(db, merchant_owner.id, store_data)
        db.commit()

        assert result["name"] == store_data["name"]
        assert result["store_code"] == store_data["store_code"].upper()
        assert result["is_active"] is True
        assert result["is_verified"] is False

    @patch.object(MerchantStoreService, "can_create_store", return_value=(True, None))
    def test_create_store_duplicate_code(self, _mock_limit, db, merchant_owner, merchant_store):
        """Test creating store with duplicate store code."""
        store_data = {
            "name": "Another Store",
            "store_code": merchant_store.store_code,
            "subdomain": f"another-{uuid.uuid4().hex[:8]}",
            "description": None,
            "platform_ids": [],
        }

        with pytest.raises(StoreAlreadyExistsException):
            self.service.create_store(db, merchant_owner.id, store_data)

    @patch.object(MerchantStoreService, "can_create_store", return_value=(True, None))
    def test_create_store_duplicate_subdomain(self, _mock_limit, db, merchant_owner, merchant_store):
        """Test creating store with duplicate subdomain."""
        store_data = {
            "name": "Another Store",
            "store_code": f"UNIQUE_{uuid.uuid4().hex[:8]}",
            "subdomain": merchant_store.subdomain,
            "description": None,
            "platform_ids": [],
        }

        with pytest.raises(StoreValidationException):
            self.service.create_store(db, merchant_owner.id, store_data)

    @patch.object(MerchantStoreService, "can_create_store", return_value=(True, None))
    def test_create_store_nonexistent_merchant(self, _mock_limit, db):
        """Test creating store for non-existent merchant."""
        store_data = {
            "name": "No Merchant Store",
            "store_code": f"NM_{uuid.uuid4().hex[:8]}",
            "subdomain": f"nm-{uuid.uuid4().hex[:8]}",
            "description": None,
            "platform_ids": [],
        }

        with pytest.raises(MerchantNotFoundException):
            self.service.create_store(db, 99999, store_data)

    @patch.object(MerchantStoreService, "can_create_store", return_value=(True, None))
    def test_create_store_creates_default_roles(self, _mock_limit, db, merchant_owner):
        """Test that default roles are created for new store."""
        from app.modules.tenancy.models.store import Role

        unique_id = str(uuid.uuid4())[:8]
        store_data = {
            "name": f"Roles Store {unique_id}",
            "store_code": f"ROLE_{unique_id}",
            "subdomain": f"role-{unique_id}".lower(),
            "description": None,
            "platform_ids": [],
        }

        result = self.service.create_store(db, merchant_owner.id, store_data)
        db.commit()

        roles = db.query(Role).filter(Role.store_id == result["id"]).all()
        role_names = {r.name for r in roles}

        assert "Owner" in role_names
        assert "Manager" in role_names
        assert "Editor" in role_names
        assert "Viewer" in role_names
