"""Tests for SME-Hub marketplace services (SP3b).

Coverage:
* FRSME-MPL014 — product publish + unpublish lifecycle
* FRSME-MPL015 — market demand notifies sector-matched entrepreneurs
* FRSME-MPL016 — demand response submission + decision flow
"""
from __future__ import annotations

import pytest
from django.contrib.auth import get_user_model
from django.utils import timezone

from apps.core.notifications.models import Notification
from apps.core.permissions.roles import UserRole
from apps.smehub.marketplace import services
from apps.smehub.marketplace.models import (
    BuyerRegistryEntry,
    DemandResponse,
    MarketDemandListing,
    ProductListing,
)
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 buyer(admin):
    user = User.objects.create_user(email="buyer@example.com", password="x", role=UserRole.BUYER)
    entry = services.register_buyer(
        user=user,
        organisation_name="Test Buyer",
        org_type=BuyerRegistryEntry.OrgType.WHOLESALE,
        sectors_sourced=["Agriculture"],
        geographic_coverage=["UG"],
        contact_email=user.email,
        description="Test buyer.",
    )
    services.verify_buyer(entry, by_user=admin)
    return BuyerRegistryEntry.objects.get(pk=entry.pk)


# ---------------------------------------------------------------------------
# Buyer registry
# ---------------------------------------------------------------------------

def test_buyer_verification_flips_status(admin):
    user = User.objects.create_user(email="newbuyer@example.com", password="x", role=UserRole.BUYER)
    entry = services.register_buyer(
        user=user,
        organisation_name="New Buyer",
        org_type=BuyerRegistryEntry.OrgType.RETAIL,
        contact_email=user.email,
        description="x",
    )
    services.verify_buyer(entry, by_user=admin)
    refreshed = BuyerRegistryEntry.objects.get(pk=entry.pk)
    assert refreshed.verification_status == BuyerRegistryEntry.Status.VERIFIED


# ---------------------------------------------------------------------------
# Product listings (FRSME-MPL014)
# ---------------------------------------------------------------------------

def test_create_product_listing_blocked_on_unverified_business(entrepreneur):
    biz = Business.objects.create(
        entrepreneur=entrepreneur,
        name="Pending Co",
        sector="Agriculture",
        business_stage=BusinessStage.MVP,
        country="UG",
    )
    with pytest.raises(services.MarketplaceStateError):
        services.create_product_listing(
            entrepreneur=entrepreneur, business=biz,
            name="Maize", sector="Agriculture", description="x",
        )


def test_publish_unpublish_cycle(entrepreneur, verified_business, admin):
    listing = services.create_product_listing(
        entrepreneur=entrepreneur, business=verified_business,
        name="Maize 50kg", sector="Agriculture", description="bulk maize",
    )
    assert listing.status == ProductListing.Status.DRAFT
    services.publish_product(listing, by_user=entrepreneur.user)
    refreshed = ProductListing.objects.get(pk=listing.pk)
    assert refreshed.status == ProductListing.Status.PUBLISHED
    assert refreshed.published_at is not None
    services.unpublish_product(refreshed, by_user=entrepreneur.user)
    refreshed = ProductListing.objects.get(pk=listing.pk)
    assert refreshed.status == ProductListing.Status.UNPUBLISHED
    assert refreshed.unpublished_at is not None


# ---------------------------------------------------------------------------
# Market demand (FRSME-MPL015)
# ---------------------------------------------------------------------------

def test_market_demand_blocked_on_unverified_buyer(admin):
    user = User.objects.create_user(email="b2@example.com", password="x", role=UserRole.BUYER)
    entry = services.register_buyer(
        user=user,
        organisation_name="Pending Buyer",
        org_type=BuyerRegistryEntry.OrgType.WHOLESALE,
        contact_email=user.email,
        description="x",
    )
    with pytest.raises(services.MarketplaceStateError):
        services.post_market_demand(buyer=entry, title="x", description="x")


def test_market_demand_notifies_sector_matched_entrepreneurs(buyer, entrepreneur, verified_business):
    Notification.objects.all().delete()
    listing = services.post_market_demand(
        buyer=buyer,
        title="Need maize",
        description="200t maize",
        sector_targeting=["Agriculture"],
        geographic_targeting=["UG"],
        quantity="200t",
    )
    assert listing.status == MarketDemandListing.Status.OPEN
    notes = Notification.objects.filter(recipient=entrepreneur.user, verb=Notification.Verb.SMEHUB_MARKET_DEMAND_MATCH)
    assert notes.count() == 1


def test_market_demand_skips_non_matching_entrepreneurs(buyer, admin):
    other_user = User.objects.create_user(email="other@example.com", password="x", role=UserRole.ENTREPRENEUR)
    other_profile = EntrepreneurProfile.objects.create(user=other_user, country="ZA", organisation="Other Co")
    other_profile.verify(by_user=admin)
    other_profile.save()
    other_biz = Business.objects.create(
        entrepreneur=other_profile,
        name="OtherCo",
        sector="FinTech",
        business_stage=BusinessStage.MVP,
        country="ZA",
    )
    other_biz.verify(by_user=admin)
    other_biz.save()
    Notification.objects.filter(recipient=other_user).delete()
    services.post_market_demand(
        buyer=buyer,
        title="Need cassava",
        description="2t cassava",
        sector_targeting=["Agriculture"],
        geographic_targeting=["UG"],
    )
    notes = Notification.objects.filter(recipient=other_user, verb=Notification.Verb.SMEHUB_MARKET_DEMAND_MATCH)
    assert notes.count() == 0


# ---------------------------------------------------------------------------
# Demand responses (FRSME-MPL016)
# ---------------------------------------------------------------------------

def test_respond_to_demand_creates_response_and_notifies_buyer(buyer, entrepreneur, verified_business):
    listing = services.post_market_demand(
        buyer=buyer, title="Maize", description="x", sector_targeting=["Agriculture"], geographic_targeting=["UG"],
    )
    Notification.objects.filter(recipient=buyer.user).delete()
    response = services.respond_to_demand(
        demand_listing=listing,
        entrepreneur=entrepreneur,
        business=verified_business,
        message="We can supply.",
        pricing_offer="USD 30/50kg",
    )
    assert response.status == DemandResponse.Status.SUBMITTED
    notes = Notification.objects.filter(recipient=buyer.user, verb=Notification.Verb.SMEHUB_DEMAND_RESPONSE)
    assert notes.count() == 1


def test_duplicate_active_response_returns_existing(buyer, entrepreneur, verified_business):
    listing = services.post_market_demand(
        buyer=buyer, title="Maize", description="x", sector_targeting=["Agriculture"], geographic_targeting=["UG"],
    )
    first = services.respond_to_demand(
        demand_listing=listing,
        entrepreneur=entrepreneur,
        business=verified_business,
        message="A",
    )
    second = services.respond_to_demand(
        demand_listing=listing,
        entrepreneur=entrepreneur,
        business=verified_business,
        message="A again",
    )
    assert first.pk == second.pk
    assert DemandResponse.objects.filter(demand_listing=listing, business=verified_business).count() == 1


def test_decide_demand_response_sets_outcome(buyer, entrepreneur, verified_business):
    listing = services.post_market_demand(
        buyer=buyer, title="Maize", description="x", sector_targeting=["Agriculture"], geographic_targeting=["UG"],
    )
    response = services.respond_to_demand(
        demand_listing=listing,
        entrepreneur=entrepreneur,
        business=verified_business,
        message="A",
    )
    services.decide_demand_response(response, by_user=buyer.user, outcome=DemandResponse.Status.AWARDED, note="Great fit.")
    refreshed = DemandResponse.objects.get(pk=response.pk)
    assert refreshed.status == DemandResponse.Status.AWARDED
    assert "Great fit" in refreshed.decision_note


def test_close_market_demand_blocks_further_responses(buyer, entrepreneur, verified_business):
    listing = services.post_market_demand(
        buyer=buyer, title="Maize", description="x", sector_targeting=["Agriculture"], geographic_targeting=["UG"],
    )
    services.close_market_demand(listing, by_user=buyer.user)
    refreshed = MarketDemandListing.objects.get(pk=listing.pk)
    assert refreshed.status == MarketDemandListing.Status.CLOSED
    with pytest.raises(services.MarketplaceStateError):
        services.respond_to_demand(
            demand_listing=refreshed,
            entrepreneur=entrepreneur,
            business=verified_business,
            message="too late",
        )
