from decimal import Decimal

import pytest

from ..common.entities import Product
from ..common.value_objects import Money, Quantity, SKU
from . import (
    DefaultPurchaseApprovalPolicy,
    DefaultPurchaseOrderTransitionPolicy,
    InvalidPurchaseOrderError,
    InvalidPurchaseOrderLineError,
    InvalidPurchaseTransitionError,
    PurchaseOrder,
    PurchaseOrderState,
    PurchasePricingService,
    PurchaseWorkflowService,
    Supplier,
)


def test_purchase_order_calculates_amounts_with_tax() -> None:
    supplier = Supplier(name="ACME Supplies", reference="SUP-001")
    order = PurchaseOrder(supplier=supplier, tax_rate=Decimal("0.10"))
    product = Product(sku=SKU("SKU-P01"), name="Paper", price=Money(Decimal("100000")), stock_qty=100)

    order.add_line(product, Quantity(10), discount_rate=Decimal("0.05"))

    pricing = PurchasePricingService()

    assert order.amount_untaxed == Money(Decimal("1000000"))
    assert order.amount_discount == Money(Decimal("50000"))
    assert order.amount_tax == Money(Decimal("95000"))
    assert order.amount_total == Money(Decimal("1045000"))
    assert pricing.calculate_total(order) == Money(Decimal("1045000"))


def test_purchase_confirm_requires_approval_above_threshold() -> None:
    supplier = Supplier(name="Vendor A")
    order = PurchaseOrder(supplier=supplier, tax_rate=Decimal("0"))
    product = Product(sku=SKU("SKU-P02"), name="Monitor", price=Money(Decimal("2500000")), stock_qty=20)
    order.add_line(product, Quantity(2))

    workflow = PurchaseWorkflowService()
    workflow.confirm(order)

    assert order.state == PurchaseOrderState.TO_APPROVE

    workflow.approve(order)
    assert order.state == PurchaseOrderState.PURCHASE

    workflow.receive(order)
    assert order.state == PurchaseOrderState.DONE


def test_purchase_confirm_without_approval_threshold_goes_directly_to_purchase() -> None:
    supplier = Supplier(name="Vendor B")
    order = PurchaseOrder(supplier=supplier)
    product = Product(sku=SKU("SKU-P03"), name="Mouse", price=Money(Decimal("500000")), stock_qty=20)
    order.add_line(product, Quantity(2))

    workflow = PurchaseWorkflowService()
    workflow.confirm(order)

    assert order.state == PurchaseOrderState.PURCHASE


def test_purchase_transition_policy_blocks_invalid_moves() -> None:
    policy = DefaultPurchaseOrderTransitionPolicy()

    assert policy.can_transition(PurchaseOrderState.DRAFT, PurchaseOrderState.RFQ_SENT) is True
    assert policy.can_transition(PurchaseOrderState.DONE, PurchaseOrderState.DRAFT) is False

    with pytest.raises(InvalidPurchaseTransitionError):
        policy.ensure_transition(PurchaseOrderState.DONE, PurchaseOrderState.DRAFT)


def test_purchase_approval_policy_rejects_non_money_input() -> None:
    policy = DefaultPurchaseApprovalPolicy()

    with pytest.raises(TypeError):
        policy.requires_approval("bad")  # type: ignore[arg-type]


def test_purchase_order_validations() -> None:
    supplier = Supplier(name="Vendor C")
    order = PurchaseOrder(supplier=supplier)

    with pytest.raises(InvalidPurchaseOrderLineError):
        order.add_line(
            Product(sku=SKU("SKU-P04"), name="Desk", price=Money(Decimal("1000000")), stock_qty=5),
            Quantity(1),
            discount_rate=Decimal("1.5"),
        )

    order.cancel()
    with pytest.raises(InvalidPurchaseOrderError):
        order.add_line(
            Product(sku=SKU("SKU-P05"), name="Chair", price=Money(Decimal("250000")), stock_qty=5),
            Quantity(1),
        )
