"""
CLM Module — Unit Tests for Domain Entities.

تست‌های واحد لایه Domain ماژول مدیریت قراردادها.
"""
import pytest
from datetime import date, timedelta
from decimal import Decimal
from uuid import uuid4

from modules.clm.backend.domain.entities.contract import (
    Contract, ContractCategory, ContractType, ContractStatus, ContractPriority,
)
from modules.clm.backend.domain.entities.party import ContractParty, PartyRole
from modules.clm.backend.domain.entities.clause import ContractClause, ClauseType
from modules.clm.backend.domain.entities.milestone import (
    ContractMilestone, MilestoneType, MilestoneStatus,
)
from modules.clm.backend.domain.entities.amendment import (
    ContractAmendment, AmendmentType, AmendmentStatus,
)
from modules.clm.backend.domain.entities.template import ContractTemplate
from modules.clm.backend.domain.entities.renewal import ContractRenewal, RenewalStatus
from modules.clm.backend.domain.exceptions.contract_exceptions import (
    InvalidContractStateTransition,
    ContractExpiredError,
    MaxRenewalsExceededError,
)


class TestContractEntity:
    """تست‌های موجودیت قرارداد."""

    def _make_contract(self, **overrides):
        defaults = {
            "id": uuid4(),
            "tenant_id": uuid4(),
            "contract_number": "CNT-1404-0001",
            "title": "قرارداد خرید تجهیزات",
            "category": ContractCategory.COMMERCIAL,
            "contract_type": ContractType.PURCHASE,
            "status": ContractStatus.DRAFT,
            "priority": ContractPriority.NORMAL,
            "start_date": date.today(),
            "end_date": date.today() + timedelta(days=365),
            "total_value": Decimal("1000000000"),
        }
        defaults.update(overrides)
        return Contract(**defaults)

    def test_create_contract(self):
        c = self._make_contract()
        assert c.title == "قرارداد خرید تجهیزات"
        assert c.status == ContractStatus.DRAFT
        assert c.category == ContractCategory.COMMERCIAL

    def test_submit_for_approval(self):
        c = self._make_contract(status=ContractStatus.DRAFT)
        c.submit_for_approval()
        assert c.status == ContractStatus.PENDING_APPROVAL

    def test_submit_for_approval_invalid_state(self):
        c = self._make_contract(status=ContractStatus.ACTIVE)
        with pytest.raises(InvalidContractStateTransition):
            c.submit_for_approval()

    def test_approve_from_pending(self):
        c = self._make_contract(status=ContractStatus.PENDING_APPROVAL)
        c.approve()
        assert c.status == ContractStatus.APPROVED

    def test_approve_invalid_state(self):
        c = self._make_contract(status=ContractStatus.ACTIVE)
        with pytest.raises(InvalidContractStateTransition):
            c.approve()

    def test_reject(self):
        c = self._make_contract(status=ContractStatus.PENDING_APPROVAL)
        c.reject()
        assert c.status == ContractStatus.DRAFT

    def test_activate(self):
        c = self._make_contract(status=ContractStatus.APPROVED)
        c.activate()
        assert c.status == ContractStatus.ACTIVE

    def test_activate_invalid_state(self):
        c = self._make_contract(status=ContractStatus.DRAFT)
        with pytest.raises(InvalidContractStateTransition):
            c.activate()

    def test_suspend(self):
        c = self._make_contract(status=ContractStatus.ACTIVE)
        c.suspend()
        assert c.status == ContractStatus.SUSPENDED

    def test_resume(self):
        c = self._make_contract(status=ContractStatus.SUSPENDED)
        c.resume()
        assert c.status == ContractStatus.ACTIVE

    def test_complete(self):
        c = self._make_contract(status=ContractStatus.ACTIVE)
        c.complete()
        assert c.status == ContractStatus.COMPLETED

    def test_terminate(self):
        c = self._make_contract(status=ContractStatus.ACTIVE)
        c.terminate()
        assert c.status == ContractStatus.TERMINATED

    def test_cancel_from_draft(self):
        c = self._make_contract(status=ContractStatus.DRAFT)
        c.cancel()
        assert c.status == ContractStatus.CANCELLED

    def test_cancel_from_completed_fails(self):
        c = self._make_contract(status=ContractStatus.COMPLETED)
        with pytest.raises(InvalidContractStateTransition):
            c.cancel()

    def test_mark_expired(self):
        c = self._make_contract(status=ContractStatus.ACTIVE)
        c.mark_expired()
        assert c.status == ContractStatus.EXPIRED

    def test_is_expired_property(self):
        c = self._make_contract(
            status=ContractStatus.ACTIVE,
            expiry_date=date.today() - timedelta(days=1),
        )
        assert c.is_expired is True

    def test_is_not_expired(self):
        c = self._make_contract(
            status=ContractStatus.ACTIVE,
            expiry_date=date.today() + timedelta(days=30),
        )
        assert c.is_expired is False

    def test_days_to_expiry(self):
        c = self._make_contract(
            status=ContractStatus.ACTIVE,
            expiry_date=date.today() + timedelta(days=10),
        )
        assert c.days_to_expiry == 10

    def test_can_renew(self):
        c = self._make_contract(
            status=ContractStatus.ACTIVE,
            max_renewals=3,
            renewals_count=0,
        )
        assert c.can_renew is True

    def test_cannot_renew_max_reached(self):
        c = self._make_contract(
            status=ContractStatus.ACTIVE,
            max_renewals=2,
            renewals_count=2,
        )
        assert c.can_renew is False

    def test_net_value(self):
        c = self._make_contract(
            total_value=Decimal("1000000"),
            advance_payment_percent=Decimal("10"),
            retention_percent=Decimal("5"),
        )
        expected = Decimal("1000000") * (1 - Decimal("5") / 100)
        assert c.net_value == expected

    def test_full_lifecycle(self):
        """تست چرخه کامل عمر قرارداد."""
        c = self._make_contract()
        assert c.status == ContractStatus.DRAFT

        c.submit_for_approval()
        assert c.status == ContractStatus.PENDING_APPROVAL

        c.approve()
        assert c.status == ContractStatus.APPROVED

        c.activate()
        assert c.status == ContractStatus.ACTIVE

        c.suspend()
        assert c.status == ContractStatus.SUSPENDED

        c.resume()
        assert c.status == ContractStatus.ACTIVE

        c.complete()
        assert c.status == ContractStatus.COMPLETED


class TestContractPartyEntity:
    """تست‌های طرف قرارداد."""

    def test_create_party(self):
        party = ContractParty(
            id=uuid4(),
            tenant_id=uuid4(),
            contract_id=uuid4(),
            party_role=PartyRole.FIRST_PARTY,
            signer_name="محمد احمدی",
        )
        assert party.party_role == PartyRole.FIRST_PARTY
        assert party.signer_name == "محمد احمدی"


class TestContractClauseEntity:
    """تست‌های بند قرارداد."""

    def test_create_clause(self):
        clause = ContractClause(
            id=uuid4(),
            tenant_id=uuid4(),
            contract_id=uuid4(),
            clause_number="1",
            title="بند عمومی",
            content="متن بند",
            clause_type=ClauseType.GENERAL,
        )
        assert clause.clause_type == ClauseType.GENERAL
        assert clause.is_mandatory is False


class TestContractMilestoneEntity:
    """تست‌های مرحله قرارداد."""

    def _make_milestone(self, **overrides):
        defaults = {
            "id": uuid4(),
            "tenant_id": uuid4(),
            "contract_id": uuid4(),
            "title": "پرداخت اول",
            "milestone_type": MilestoneType.PAYMENT,
            "status": MilestoneStatus.PENDING,
            "planned_date": date.today() + timedelta(days=30),
            "amount": Decimal("500000000"),
        }
        defaults.update(overrides)
        return ContractMilestone(**defaults)

    def test_complete_milestone(self):
        m = self._make_milestone()
        m.complete(actual_date=date.today())
        assert m.status == MilestoneStatus.COMPLETED

    def test_is_overdue(self):
        m = self._make_milestone(
            status=MilestoneStatus.PENDING,
            due_date=date.today() - timedelta(days=5),
        )
        assert m.is_overdue is True

    def test_delay_days(self):
        m = self._make_milestone(
            status=MilestoneStatus.PENDING,
            due_date=date.today() - timedelta(days=10),
        )
        assert m.delay_days == 10


class TestContractAmendmentEntity:
    """تست‌های الحاقیه."""

    def _make_amendment(self, **overrides):
        defaults = {
            "id": uuid4(),
            "tenant_id": uuid4(),
            "contract_id": uuid4(),
            "amendment_number": 1,
            "title": "الحاقیه افزایش مبلغ",
            "amendment_type": AmendmentType.VALUE_CHANGE,
            "status": AmendmentStatus.DRAFT,
        }
        defaults.update(overrides)
        return ContractAmendment(**defaults)

    def test_approve_amendment(self):
        a = self._make_amendment(status=AmendmentStatus.PENDING_APPROVAL)
        a.approve(approved_by=uuid4())
        assert a.status == AmendmentStatus.APPROVED

    def test_reject_amendment(self):
        a = self._make_amendment(status=AmendmentStatus.PENDING_APPROVAL)
        a.reject(rejected_by=uuid4(), reason="مبلغ نامعقول")
        assert a.status == AmendmentStatus.REJECTED


class TestContractTemplateEntity:
    """تست‌های قالب قرارداد."""

    def test_create_template(self):
        template = ContractTemplate(
            id=uuid4(),
            tenant_id=uuid4(),
            name="قالب قرارداد خرید",
            category=ContractCategory.COMMERCIAL,
            contract_type=ContractType.PURCHASE,
        )
        assert template.is_active is True


class TestContractRenewalEntity:
    """تست‌های تمدید قرارداد."""

    def test_create_renewal(self):
        renewal = ContractRenewal(
            id=uuid4(),
            tenant_id=uuid4(),
            contract_id=uuid4(),
            renewal_number=1,
            old_start_date=date.today() - timedelta(days=365),
            old_end_date=date.today(),
            new_start_date=date.today(),
            new_end_date=date.today() + timedelta(days=365),
            old_value=Decimal("1000000000"),
            new_value=Decimal("1100000000"),
            status=RenewalStatus.APPROVED,
        )
        assert renewal.renewal_number == 1
        assert renewal.status == RenewalStatus.APPROVED
