"""Access-control tests for the Phase 6.3 MarketValidation + SalesRecord views."""
from __future__ import annotations

from datetime import date
from decimal import Decimal

import pytest
from django.contrib.auth import get_user_model
from django.test import Client
from django.urls import reverse

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

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


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------

@pytest.fixture
def admin_user():
    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_user(admin_user):
    user = User.objects.create_user(
        email="ent@example.com", password="x", role=UserRole.ENTREPRENEUR,
    )
    profile = EntrepreneurProfile.objects.create(user=user, country="UG", organisation="Ent Co")
    profile.verify(by_user=admin_user)
    profile.save()
    return user


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


@pytest.fixture
def learner_user():
    return User.objects.create_user(
        email="learner@example.com", password="x", role=UserRole.LEARNER,
    )


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


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


@pytest.fixture
def validation(entrepreneur_user, listing):
    return services.validate_market_fit(
        product_listing=listing,
        recorded_by=entrepreneur_user,
        method=MarketValidation.Method.INTERVIEW,
        problem_fit_score=4,
        solution_fit_score=4,
        willingness_to_pay_score=4,
    )


@pytest.fixture
def sale(entrepreneur_user, verified_business, listing):
    return services.record_sale(
        product_listing=listing,
        business=verified_business,
        entrepreneur=entrepreneur_user,
        channel=SalesRecord.Channel.DIRECT_LISTING,
        quantity=Decimal("1"),
        gross_amount=Decimal("250.00"),
        currency="USD",
        sale_date=date(2026, 4, 1),
        customer_name="Direct buyer",
    )


def _login(client, user):
    client.force_login(user)


# ---------------------------------------------------------------------------
# MarketValidation
# ---------------------------------------------------------------------------

def test_validation_list_requires_login(listing):
    client = Client()
    resp = client.get(reverse("smehub_marketplace:market_validation_list", args=[listing.pk]))
    assert resp.status_code == 302


def test_validation_list_blocks_other_entrepreneur(listing, other_entrepreneur_user):
    client = Client()
    _login(client, other_entrepreneur_user)
    resp = client.get(reverse("smehub_marketplace:market_validation_list", args=[listing.pk]))
    assert resp.status_code == 403


def test_validation_list_allows_owner(listing, entrepreneur_user):
    client = Client()
    _login(client, entrepreneur_user)
    resp = client.get(reverse("smehub_marketplace:market_validation_list", args=[listing.pk]))
    assert resp.status_code == 200


def test_validation_list_allows_admin(listing, admin_user):
    client = Client()
    _login(client, admin_user)
    resp = client.get(reverse("smehub_marketplace:market_validation_list", args=[listing.pk]))
    assert resp.status_code == 200


def test_validation_create_blocks_other_entrepreneur(listing, other_entrepreneur_user):
    client = Client()
    _login(client, other_entrepreneur_user)
    resp = client.get(reverse("smehub_marketplace:market_validation_create", args=[listing.pk]))
    assert resp.status_code == 403


def test_validation_create_blocks_random_user(listing, learner_user):
    client = Client()
    _login(client, learner_user)
    resp = client.get(reverse("smehub_marketplace:market_validation_create", args=[listing.pk]))
    assert resp.status_code == 403


def test_validation_detail_blocks_other_entrepreneur(listing, validation, other_entrepreneur_user):
    client = Client()
    _login(client, other_entrepreneur_user)
    resp = client.get(
        reverse("smehub_marketplace:market_validation_detail", args=[listing.pk, validation.pk]),
    )
    assert resp.status_code == 403


# ---------------------------------------------------------------------------
# SalesRecord
# ---------------------------------------------------------------------------

def test_sale_list_requires_login():
    client = Client()
    resp = client.get(reverse("smehub_marketplace:sale_list"))
    assert resp.status_code == 302


def test_sale_list_excludes_other_entrepreneur_sales(sale, other_entrepreneur_user):
    """Other entrepreneurs see an empty list, not someone else's sales."""
    client = Client()
    _login(client, other_entrepreneur_user)
    resp = client.get(reverse("smehub_marketplace:sale_list"))
    assert resp.status_code == 200
    # The sale belongs to entrepreneur_user; should not be in this user's queryset.
    assert sale not in resp.context["sales"]


def test_sale_list_shows_own_sales(sale, entrepreneur_user):
    client = Client()
    _login(client, entrepreneur_user)
    resp = client.get(reverse("smehub_marketplace:sale_list"))
    assert resp.status_code == 200
    assert sale in resp.context["sales"]


def test_sale_create_blocks_non_entrepreneur(learner_user):
    client = Client()
    _login(client, learner_user)
    resp = client.get(reverse("smehub_marketplace:sale_create"))
    assert resp.status_code == 403


def test_sale_detail_blocks_other_entrepreneur(sale, other_entrepreneur_user):
    client = Client()
    _login(client, other_entrepreneur_user)
    resp = client.get(reverse("smehub_marketplace:sale_detail", args=[sale.pk]))
    assert resp.status_code == 403


def test_sale_detail_allows_admin(sale, admin_user):
    client = Client()
    _login(client, admin_user)
    resp = client.get(reverse("smehub_marketplace:sale_detail", args=[sale.pk]))
    assert resp.status_code == 200


def test_sale_update_blocks_other_entrepreneur(sale, other_entrepreneur_user):
    client = Client()
    _login(client, other_entrepreneur_user)
    resp = client.get(reverse("smehub_marketplace:sale_update", args=[sale.pk]))
    assert resp.status_code == 403


def test_sale_verify_blocks_owner(sale, entrepreneur_user):
    """Entrepreneurs can't self-verify their own sales."""
    client = Client()
    _login(client, entrepreneur_user)
    resp = client.post(reverse("smehub_marketplace:sale_verify", args=[sale.pk]))
    assert resp.status_code == 403


def test_sale_verify_allows_admin(sale, admin_user):
    client = Client()
    _login(client, admin_user)
    resp = client.post(reverse("smehub_marketplace:sale_verify", args=[sale.pk]))
    assert resp.status_code == 302  # redirects on success
    sale.refresh_from_db()
    assert sale.status == SalesRecord.Status.VERIFIED


def test_sale_dispute_blocks_other_entrepreneur(sale, other_entrepreneur_user):
    client = Client()
    _login(client, other_entrepreneur_user)
    resp = client.post(
        reverse("smehub_marketplace:sale_dispute", args=[sale.pk]),
        {"reason": "x"},
    )
    assert resp.status_code == 403


def test_sale_cancel_blocks_other_entrepreneur(sale, other_entrepreneur_user):
    client = Client()
    _login(client, other_entrepreneur_user)
    resp = client.post(reverse("smehub_marketplace:sale_cancel", args=[sale.pk]))
    assert resp.status_code == 403


# ---------------------------------------------------------------------------
# Admin manual-entry path (PRD A5 — admin records a sale on behalf of entrepreneur)
# ---------------------------------------------------------------------------

def test_sale_create_admin_path_requires_business_param(admin_user):
    client = Client()
    _login(client, admin_user)
    resp = client.get(reverse("smehub_marketplace:sale_create"))
    assert resp.status_code == 403  # admin without ?business= → forbidden


def test_sale_create_admin_path_allows_with_business(admin_user, verified_business):
    client = Client()
    _login(client, admin_user)
    resp = client.get(
        reverse("smehub_marketplace:sale_create") + f"?business={verified_business.pk}",
    )
    assert resp.status_code == 200
    assert resp.context["business"].pk == verified_business.pk


def test_sale_create_admin_path_records_with_entrepreneur_attribution(
    admin_user, verified_business, entrepreneur_user,
):
    """Admin records the sale; the resulting record's entrepreneur is the
    business's owning user, not the admin (PRD A5 attribution rule)."""
    client = Client()
    _login(client, admin_user)
    resp = client.post(
        reverse("smehub_marketplace:sale_create") + f"?business={verified_business.pk}",
        {
            "business": verified_business.pk,
            "channel": SalesRecord.Channel.OFFLINE,
            "quantity": "1",
            "unit": "units",
            "gross_amount": "150.00",
            "currency": "USD",
            "sale_date": "2026-04-15",
            "customer_name": "Walk-in",
        },
    )
    assert resp.status_code == 302
    sale = SalesRecord.objects.get(business=verified_business, gross_amount=150)
    # The recording entrepreneur is the business owner, not the admin.
    assert sale.entrepreneur_id == entrepreneur_user.pk
