"""Tests for SME-Hub SalesRecord (Phase 6.3).

Coverage:
* record_sale creates a RECORDED row, derives unit_price from gross/qty
* Auto-stub on AWARDED demand response (idempotent, only on AWARDED)
* update_sale_record promotes PENDING_DETAILS → RECORDED when complete
* verify / dispute / cancel transitions
* MEL event emission on sale_recorded + sale_verified
"""
from __future__ import annotations

from datetime import date
from decimal import Decimal

import pytest
from django.contrib.auth import get_user_model

from apps.core.permissions.roles import UserRole
from apps.smehub.marketplace import services
from apps.smehub.marketplace.models import (
    BuyerRegistryEntry,
    DemandResponse,
    MarketDemandListing,
    SalesRecord,
)
from apps.smehub.onboarding.models import (
    Business,
    BusinessStage,
    EntrepreneurProfile,
)

User = get_user_model()
pytestmark = pytest.mark.django_db


@pytest.fixture
def admin():
    return User.objects.create_user(
        email="admin@example.com", password="x", is_superuser=True, is_staff=True,
        role=UserRole.SYSTEM_ADMIN,
    )


@pytest.fixture
def entrepreneur(admin):
    user = User.objects.create_user(email="ent@example.com", password="x", role=UserRole.ENTREPRENEUR)
    profile = EntrepreneurProfile.objects.create(user=user, country="UG", organisation="Test Co")
    profile.verify(by_user=admin)
    profile.save()
    return profile


@pytest.fixture
def verified_business(entrepreneur, admin):
    biz = Business.objects.create(
        entrepreneur=entrepreneur,
        name="GrainCo",
        sector="Agriculture",
        business_stage=BusinessStage.MVP,
        country="UG",
    )
    biz.verify(by_user=admin)
    biz.save()
    return biz


@pytest.fixture
def listing(entrepreneur, verified_business):
    return services.create_product_listing(
        entrepreneur=entrepreneur,
        business=verified_business,
        name="Solar dryer",
        sector="Agriculture",
        description="x",
    )


@pytest.fixture
def buyer(admin):
    user = User.objects.create_user(email="buyer@example.com", password="x", role=UserRole.BUYER)
    entry = services.register_buyer(
        user=user,
        organisation_name="AgriBuy",
        org_type=BuyerRegistryEntry.OrgType.WHOLESALE,
        sectors_sourced=["Agriculture"],
        geographic_coverage=["UG"],
        contact_email=user.email,
    )
    services.verify_buyer(entry, by_user=admin)
    return BuyerRegistryEntry.objects.get(pk=entry.pk)


# ---------------------------------------------------------------------------
# record_sale
# ---------------------------------------------------------------------------

def test_record_sale_creates_recorded_state(entrepreneur, verified_business, listing):
    sale = services.record_sale(
        product_listing=listing,
        business=verified_business,
        entrepreneur=entrepreneur.user,
        channel=SalesRecord.Channel.DIRECT_LISTING,
        quantity=Decimal("10"),
        gross_amount=Decimal("250.00"),
        currency="USD",
        sale_date=date(2026, 4, 1),
        customer_name="Direct buyer",
    )
    assert sale.status == SalesRecord.Status.RECORDED
    assert sale.unit_price == Decimal("25.00")  # 250 / 10


def test_record_sale_requires_customer_name_or_buyer(entrepreneur, verified_business, listing):
    with pytest.raises(services.MarketplaceStateError):
        services.record_sale(
            product_listing=listing,
            business=verified_business,
            entrepreneur=entrepreneur.user,
            channel=SalesRecord.Channel.OFFLINE,
            quantity=Decimal("1"),
            gross_amount=Decimal("100"),
            currency="USD",
            sale_date=date(2026, 4, 1),
        )


# ---------------------------------------------------------------------------
# Auto-stub from awarded demand response
# ---------------------------------------------------------------------------

def _award_response(entrepreneur, verified_business, buyer, admin, listing):
    services.publish_product(listing, by_user=entrepreneur.user)
    demand = services.post_market_demand(
        buyer=buyer,
        title="Need 5 tonnes maize",
        description="Looking for maize supplier.",
        sector_targeting=["Agriculture"],
        geographic_targeting=["UG"],
    )
    response = services.respond_to_demand(
        demand_listing=demand,
        entrepreneur=entrepreneur,
        business=verified_business,
        message="We can supply 5 tonnes.",
        product_listing=listing,
    )
    services.decide_demand_response(
        response, by_user=admin, outcome=DemandResponse.Status.AWARDED, note="Selected.",
    )
    return response


def test_award_creates_sale_stub(entrepreneur, verified_business, buyer, admin, listing):
    response = _award_response(entrepreneur, verified_business, buyer, admin, listing)
    sale = SalesRecord.objects.get(demand_response=response)
    assert sale.status == SalesRecord.Status.PENDING_DETAILS
    assert sale.channel == SalesRecord.Channel.MARKETPLACE_AWARD
    assert sale.product_listing_id == listing.pk
    assert sale.buyer_entry_id == buyer.pk


def test_award_stub_is_idempotent(entrepreneur, verified_business, buyer, admin, listing):
    response = _award_response(entrepreneur, verified_business, buyer, admin, listing)
    # Calling the helper a second time must not double-create.
    services._create_sale_stub_from_award(response)
    assert SalesRecord.objects.filter(demand_response=response).count() == 1


def test_non_award_outcomes_dont_create_stub(
    entrepreneur, verified_business, buyer, admin, listing,
):
    services.publish_product(listing, by_user=entrepreneur.user)
    demand = services.post_market_demand(
        buyer=buyer, title="Need produce", description="x",
        sector_targeting=["Agriculture"], geographic_targeting=["UG"],
    )
    response = services.respond_to_demand(
        demand_listing=demand, entrepreneur=entrepreneur,
        business=verified_business, message="x",
    )
    services.decide_demand_response(
        response, by_user=admin, outcome=DemandResponse.Status.SHORTLISTED,
    )
    assert not SalesRecord.objects.filter(demand_response=response).exists()


# ---------------------------------------------------------------------------
# update / verify / dispute / cancel
# ---------------------------------------------------------------------------

def test_update_sale_record_promotes_pending_to_recorded_when_complete(
    entrepreneur, verified_business, buyer, admin, listing,
):
    response = _award_response(entrepreneur, verified_business, buyer, admin, listing)
    sale = SalesRecord.objects.get(demand_response=response)
    assert sale.status == SalesRecord.Status.PENDING_DETAILS
    services.update_sale_record(
        sale,
        by_user=entrepreneur.user,
        gross_amount=Decimal("500.00"),
        quantity=Decimal("5"),
        sale_date=date(2026, 4, 15),
    )
    sale.refresh_from_db()
    assert sale.status == SalesRecord.Status.RECORDED
    assert sale.unit_price == Decimal("100.00")


def test_verify_sale_admin_only(entrepreneur, verified_business, listing, admin):
    sale = services.record_sale(
        product_listing=listing,
        business=verified_business,
        entrepreneur=entrepreneur.user,
        channel=SalesRecord.Channel.DIRECT_LISTING,
        quantity=Decimal("1"),
        gross_amount=Decimal("99.00"),
        currency="USD",
        sale_date=date(2026, 4, 1),
        customer_name="X",
    )
    services.verify_sale(sale, by_user=admin)
    sale.refresh_from_db()
    assert sale.status == SalesRecord.Status.VERIFIED
    assert sale.verified_by_id == admin.pk


def test_dispute_sale_lifecycle(entrepreneur, verified_business, listing, admin):
    sale = services.record_sale(
        product_listing=listing,
        business=verified_business,
        entrepreneur=entrepreneur.user,
        channel=SalesRecord.Channel.DIRECT_LISTING,
        quantity=Decimal("1"),
        gross_amount=Decimal("99"),
        currency="USD",
        sale_date=date(2026, 4, 1),
        customer_name="X",
    )
    services.dispute_sale(sale, by_user=admin, reason="Buyer denies receipt.")
    sale.refresh_from_db()
    assert sale.status == SalesRecord.Status.DISPUTED
    assert sale.dispute_reason


def test_cancel_sale_terminal(entrepreneur, verified_business, listing):
    sale = services.record_sale(
        product_listing=listing,
        business=verified_business,
        entrepreneur=entrepreneur.user,
        channel=SalesRecord.Channel.DIRECT_LISTING,
        quantity=Decimal("1"),
        gross_amount=Decimal("99"),
        currency="USD",
        sale_date=date(2026, 4, 1),
        customer_name="X",
    )
    services.cancel_sale(sale, by_user=entrepreneur.user, reason="Order returned.")
    sale.refresh_from_db()
    assert sale.status == SalesRecord.Status.CANCELLED


# ---------------------------------------------------------------------------
# MEL event emission
# ---------------------------------------------------------------------------

def test_sale_recorded_emits_mel_event(entrepreneur, verified_business, listing):
    from apps.mel.tracking.models import TrackingMilestone

    sale = services.record_sale(
        product_listing=listing,
        business=verified_business,
        entrepreneur=entrepreneur.user,
        channel=SalesRecord.Channel.DIRECT_LISTING,
        quantity=Decimal("2"),
        gross_amount=Decimal("250.00"),
        currency="UGX",
        sale_date=date(2026, 4, 5),
        customer_name="Walk-in",
    )
    milestone = TrackingMilestone.objects.get(
        event_type="smehub_sale_recorded", source_id=str(sale.pk),
    )
    assert milestone.payload["channel"] == SalesRecord.Channel.DIRECT_LISTING
    assert milestone.payload["currency"] == "UGX"
    assert milestone.payload["gross_amount"] == "250.00"


def test_sale_verified_emits_mel_event(entrepreneur, verified_business, listing, admin):
    from apps.mel.tracking.models import TrackingMilestone

    sale = services.record_sale(
        product_listing=listing,
        business=verified_business,
        entrepreneur=entrepreneur.user,
        channel=SalesRecord.Channel.DIRECT_LISTING,
        quantity=Decimal("1"),
        gross_amount=Decimal("99.00"),
        currency="USD",
        sale_date=date(2026, 4, 6),
        customer_name="X",
    )
    services.verify_sale(sale, by_user=admin)
    assert TrackingMilestone.objects.filter(
        event_type="smehub_sale_verified", source_id=str(sale.pk),
    ).exists()
