from decimal import Decimal

import pytest

from ..common.entities import Customer, Product
from ..common.value_objects import Money, Quantity, SKU
from . import (
    DefaultSalesOrderTransitionPolicy,
    InvalidSalesOrderError,
    InvalidSalesOrderLineError,
    InvalidSalesTransitionError,
    SalesOrder,
    SalesOrderState,
    SalesPricingService,
    SalesWorkflowService,
)


def test_sales_order_amounts_and_discounts() -> None:
    customer = Customer(name="Alice")
    order = SalesOrder(customer=customer)
    product = Product(sku=SKU("SKU-900"), name="Desk", price=Money(Decimal("1000000")), stock_qty=10)

    order.add_line(product, Quantity(3), discount_rate=Decimal("0.10"))

    pricing = SalesPricingService()
    assert order.amount_untaxed == Money(Decimal("3000000"))
    assert order.amount_discount == Money(Decimal("300000"))
    assert order.amount_total == Money(Decimal("2700000"))
    assert pricing.calculate_total(order) == Money(Decimal("2700000"))


def test_sales_workflow_transitions_follow_default_policy() -> None:
    customer = Customer(name="Bob")
    order = SalesOrder(customer=customer)
    workflow = SalesWorkflowService()

    workflow.send(order)
    assert order.state == SalesOrderState.SENT

    workflow.confirm(order)
    assert order.state == SalesOrderState.SALE

    workflow.complete(order)
    assert order.state == SalesOrderState.DONE

    with pytest.raises(InvalidSalesOrderError):
        order.add_line(
            Product(sku=SKU("SKU-901"), name="Chair", price=Money(Decimal("250000")), stock_qty=10),
            Quantity(1),
        )


def test_sales_policy_rejects_invalid_backwards_transition() -> None:
    policy = DefaultSalesOrderTransitionPolicy()
    assert policy.can_transition(SalesOrderState.QUOTATION, SalesOrderState.SENT) is True
    assert policy.can_transition(SalesOrderState.DONE, SalesOrderState.QUOTATION) is False

    with pytest.raises(InvalidSalesTransitionError):
        policy.ensure_transition(SalesOrderState.DONE, SalesOrderState.QUOTATION)


def test_sales_order_line_validations() -> None:
    customer = Customer(name="Carol")
    order = SalesOrder(customer=customer)
    product = Product(sku=SKU("SKU-902"), name="Laptop", price=Money(Decimal("5000000")), stock_qty=5)

    with pytest.raises(InvalidSalesOrderLineError):
        order.add_line(product, Quantity(1), discount_rate=Decimal("1.5"))
