# tests/unit/services/test_company_service.py
"""Unit tests for CompanyService."""

import uuid

import pytest

from app.modules.tenancy.exceptions import CompanyNotFoundException, UserNotFoundException
from app.modules.tenancy.services.company_service import company_service
from app.modules.tenancy.models import Company
from app.modules.tenancy.schemas.company import (
    CompanyCreate,
    CompanyUpdate,
    CompanyTransferOwnership,
)


# =============================================================================
# FIXTURES
# =============================================================================


@pytest.fixture
def unverified_company(db, test_user):
    """Create an unverified test company."""
    unique_id = str(uuid.uuid4())[:8]
    company = Company(
        name=f"Unverified Company {unique_id}",
        owner_user_id=test_user.id,
        contact_email=f"unverified{unique_id}@company.com",
        is_active=True,
        is_verified=False,
    )
    db.add(company)
    db.commit()
    db.refresh(company)
    return company


@pytest.fixture
def inactive_company(db, test_user):
    """Create an inactive test company."""
    unique_id = str(uuid.uuid4())[:8]
    company = Company(
        name=f"Inactive Company {unique_id}",
        owner_user_id=test_user.id,
        contact_email=f"inactive{unique_id}@company.com",
        is_active=False,
        is_verified=True,
    )
    db.add(company)
    db.commit()
    db.refresh(company)
    return company


# =============================================================================
# CREATE TESTS
# =============================================================================


@pytest.mark.unit
@pytest.mark.tenancy
class TestCompanyServiceCreate:
    """Test suite for company creation."""

    def test_create_company_with_new_owner(self, db):
        """Test creating a company with a new owner user."""
        unique_id = str(uuid.uuid4())[:8]
        company_data = CompanyCreate(
            name=f"New Company {unique_id}",
            owner_email=f"newowner{unique_id}@example.com",
            contact_email=f"contact{unique_id}@company.com",
            description="A new test company",
        )

        company, owner, temp_password = company_service.create_company_with_owner(
            db, company_data
        )
        db.commit()

        assert company is not None
        assert company.name == f"New Company {unique_id}"
        assert company.is_active is True
        assert company.is_verified is False
        assert owner is not None
        assert owner.email == f"newowner{unique_id}@example.com"
        assert temp_password is not None  # New user gets temp password

    def test_create_company_with_existing_owner(self, db, test_user):
        """Test creating a company with an existing user as owner."""
        unique_id = str(uuid.uuid4())[:8]
        company_data = CompanyCreate(
            name=f"Existing Owner Company {unique_id}",
            owner_email=test_user.email,
            contact_email=f"contact{unique_id}@company.com",
        )

        company, owner, temp_password = company_service.create_company_with_owner(
            db, company_data
        )
        db.commit()

        assert company is not None
        assert owner.id == test_user.id
        assert temp_password is None  # Existing user doesn't get new password


# =============================================================================
# READ TESTS
# =============================================================================


@pytest.mark.unit
@pytest.mark.tenancy
class TestCompanyServiceRead:
    """Test suite for reading companies."""

    def test_get_company_by_id_success(self, db, test_company):
        """Test getting a company by ID."""
        company = company_service.get_company_by_id(db, test_company.id)

        assert company is not None
        assert company.id == test_company.id
        assert company.name == test_company.name

    def test_get_company_by_id_not_found(self, db):
        """Test getting a non-existent company raises exception."""
        with pytest.raises(CompanyNotFoundException) as exc_info:
            company_service.get_company_by_id(db, 99999)

        assert "99999" in str(exc_info.value)

    def test_get_companies_paginated(self, db, test_company, other_company):
        """Test getting paginated list of companies."""
        companies, total = company_service.get_companies(db, skip=0, limit=10)

        assert len(companies) >= 2
        assert total >= 2

    def test_get_companies_with_search(self, db, test_company):
        """Test searching companies by name."""
        # Get the unique part of the company name
        search_term = test_company.name.split()[0]  # "Test"

        companies, total = company_service.get_companies(
            db, skip=0, limit=100, search=search_term
        )

        assert len(companies) >= 1
        assert any(c.id == test_company.id for c in companies)

    def test_get_companies_filter_by_active(self, db, test_company, inactive_company):
        """Test filtering companies by active status."""
        active_companies, _ = company_service.get_companies(
            db, skip=0, limit=100, is_active=True
        )
        inactive_companies, _ = company_service.get_companies(
            db, skip=0, limit=100, is_active=False
        )

        assert all(c.is_active for c in active_companies)
        assert all(not c.is_active for c in inactive_companies)

    def test_get_companies_filter_by_verified(self, db, test_company, unverified_company):
        """Test filtering companies by verified status."""
        verified_companies, _ = company_service.get_companies(
            db, skip=0, limit=100, is_verified=True
        )
        unverified_companies, _ = company_service.get_companies(
            db, skip=0, limit=100, is_verified=False
        )

        assert all(c.is_verified for c in verified_companies)
        assert all(not c.is_verified for c in unverified_companies)


# =============================================================================
# UPDATE TESTS
# =============================================================================


@pytest.mark.unit
@pytest.mark.tenancy
class TestCompanyServiceUpdate:
    """Test suite for updating companies."""

    def test_update_company_success(self, db, test_company):
        """Test updating company information."""
        unique_id = str(uuid.uuid4())[:8]
        update_data = CompanyUpdate(
            name=f"Updated Company {unique_id}",
            description="Updated description",
        )

        updated = company_service.update_company(db, test_company.id, update_data)
        db.commit()

        assert updated.name == f"Updated Company {unique_id}"
        assert updated.description == "Updated description"

    def test_update_company_partial(self, db, test_company):
        """Test partial update of company."""
        original_name = test_company.name
        update_data = CompanyUpdate(description="Only description updated")

        updated = company_service.update_company(db, test_company.id, update_data)
        db.commit()

        assert updated.name == original_name  # Name unchanged
        assert updated.description == "Only description updated"

    def test_update_company_not_found(self, db):
        """Test updating non-existent company raises exception."""
        update_data = CompanyUpdate(name="New Name")

        with pytest.raises(CompanyNotFoundException):
            company_service.update_company(db, 99999, update_data)


# =============================================================================
# DELETE TESTS
# =============================================================================


@pytest.mark.unit
@pytest.mark.tenancy
class TestCompanyServiceDelete:
    """Test suite for deleting companies."""

    def test_delete_company_success(self, db, test_user):
        """Test deleting a company."""
        # Create a company to delete
        unique_id = str(uuid.uuid4())[:8]
        company = Company(
            name=f"To Delete {unique_id}",
            owner_user_id=test_user.id,
            contact_email=f"delete{unique_id}@company.com",
            is_active=True,
        )
        db.add(company)
        db.commit()
        company_id = company.id

        # Delete it
        company_service.delete_company(db, company_id)
        db.commit()

        # Verify it's gone
        with pytest.raises(CompanyNotFoundException):
            company_service.get_company_by_id(db, company_id)

    def test_delete_company_not_found(self, db):
        """Test deleting non-existent company raises exception."""
        with pytest.raises(CompanyNotFoundException):
            company_service.delete_company(db, 99999)


# =============================================================================
# TOGGLE TESTS
# =============================================================================


@pytest.mark.unit
@pytest.mark.tenancy
class TestCompanyServiceToggle:
    """Test suite for toggling company status."""

    def test_toggle_verification_on(self, db, unverified_company):
        """Test setting verification to True."""
        result = company_service.toggle_verification(db, unverified_company.id, True)
        db.commit()

        assert result.is_verified is True

    def test_toggle_verification_off(self, db, test_company):
        """Test setting verification to False."""
        result = company_service.toggle_verification(db, test_company.id, False)
        db.commit()

        assert result.is_verified is False

    def test_toggle_active_on(self, db, inactive_company):
        """Test setting active status to True."""
        result = company_service.toggle_active(db, inactive_company.id, True)
        db.commit()

        assert result.is_active is True

    def test_toggle_active_off(self, db, test_company):
        """Test setting active status to False."""
        result = company_service.toggle_active(db, test_company.id, False)
        db.commit()

        assert result.is_active is False


# =============================================================================
# OWNERSHIP TRANSFER TESTS
# =============================================================================


@pytest.mark.unit
@pytest.mark.tenancy
class TestCompanyServiceOwnershipTransfer:
    """Test suite for company ownership transfer."""

    def test_transfer_ownership_success(self, db, test_company, other_user):
        """Test successful ownership transfer."""
        original_owner_id = test_company.owner_user_id
        transfer_data = CompanyTransferOwnership(
            new_owner_user_id=other_user.id,
            transfer_reason="Testing ownership transfer",
        )

        company, old_owner, new_owner = company_service.transfer_ownership(
            db, test_company.id, transfer_data
        )
        db.commit()

        assert company.owner_user_id == other_user.id
        assert old_owner.id == original_owner_id
        assert new_owner.id == other_user.id

    def test_transfer_ownership_to_same_owner_fails(self, db, test_company, test_user):
        """Test transfer to same owner raises error."""
        transfer_data = CompanyTransferOwnership(
            new_owner_user_id=test_user.id,
            transfer_reason="This should fail",
        )

        with pytest.raises(ValueError) as exc_info:
            company_service.transfer_ownership(db, test_company.id, transfer_data)

        assert "current owner" in str(exc_info.value).lower()

    def test_transfer_ownership_to_nonexistent_user_fails(self, db, test_company):
        """Test transfer to non-existent user raises exception."""
        transfer_data = CompanyTransferOwnership(
            new_owner_user_id=99999,
            transfer_reason="This should fail",
        )

        with pytest.raises(UserNotFoundException):
            company_service.transfer_ownership(db, test_company.id, transfer_data)

    def test_transfer_ownership_nonexistent_company_fails(self, db, other_user):
        """Test transfer on non-existent company raises exception."""
        transfer_data = CompanyTransferOwnership(
            new_owner_user_id=other_user.id,
            transfer_reason="This should fail",
        )

        with pytest.raises(CompanyNotFoundException):
            company_service.transfer_ownership(db, 99999, transfer_data)
