"""Tests for Phase 4 SP4 showcasing audit fixes.

Coverage:
* FRSME-ISD A5 — ``announce_winners`` now sets ``is_challenge_winner=True``
  on each winning ``ShowcaseApplication``.
* FRSME-ISD016 alt-A3 — ``warn_expiring_deal_rooms`` warns OPEN rooms
  within ``days_before`` of expiry, idempotently (re-running does not
  re-spam).
* FRSME-ISD008 — ``InnovationChallenge.evaluation_panel`` is a real M2M
  field that gates ``PitchScoringView``-style judge access.
"""
from __future__ import annotations

from datetime import timedelta

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.onboarding.models import (
    Business,
    BusinessStage,
    EntrepreneurProfile,
)
from apps.smehub.showcasing import services
from apps.smehub.showcasing.models import (
    DealRoom,
    InnovationChallenge,
    ShowcaseApplication,
)

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


# ---------------------------------------------------------------------------
# Minimal fixtures (decoupled from the existing test_showcasing_services suite)
# ---------------------------------------------------------------------------

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


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


@pytest.fixture
def open_challenge(admin):
    challenge = services.create_challenge(
        organiser=admin,
        title="P4 Challenge",
        application_deadline=timezone.now() + timedelta(days=30),
        target_sectors=["Agriculture"],
        rubric_sections=[
            {"title": "A", "weight": "1.0", "max_marks": 10, "order": 0},
        ],
    )
    return services.publish_challenge(challenge, by_user=admin)


# ---------------------------------------------------------------------------
# FRSME-ISD A5 — Challenge winner flag
# ---------------------------------------------------------------------------

def test_announce_winners_sets_challenge_winner_flag(
    open_challenge, entrepreneur, verified_business, admin,
):
    application = services.apply_to_challenge(
        challenge=open_challenge,
        entrepreneur=entrepreneur,
        business=verified_business,
        innovation_title="Win Innovation",
    )
    services.announce_winners(open_challenge, winners=[application], by_user=admin)
    fresh = ShowcaseApplication.objects.get(pk=application.pk)
    assert fresh.is_challenge_winner is True
    assert fresh.challenge_winner_at is not None
    assert open_challenge.title in fresh.challenge_winner_notes


def test_announce_winners_emits_winner_notification(
    open_challenge, entrepreneur, verified_business, admin,
):
    application = services.apply_to_challenge(
        challenge=open_challenge,
        entrepreneur=entrepreneur,
        business=verified_business,
        innovation_title="Notif Innovation",
    )
    Notification.objects.filter(recipient=entrepreneur.user).delete()
    services.announce_winners(open_challenge, winners=[application], by_user=admin)
    msg = Notification.objects.filter(recipient=entrepreneur.user).order_by("-created_at").first()
    assert msg is not None
    assert "Notif Innovation" in msg.message


# ---------------------------------------------------------------------------
# FRSME-ISD008 — challenge evaluation panel field
# ---------------------------------------------------------------------------

def test_innovation_challenge_evaluation_panel_is_m2m(open_challenge):
    """The audit fix added ``evaluation_panel`` so judges can be scoped per
    challenge. Smoke-test that the field accepts users."""
    judge = User.objects.create_user(
        email="judge-p4@example.com", password="x", role=UserRole.JUDGE,
    )
    open_challenge.evaluation_panel.add(judge)
    assert list(open_challenge.evaluation_panel.all()) == [judge]


# ---------------------------------------------------------------------------
# FRSME-ISD016 alt-A3 — Deal Room expiry warning
# ---------------------------------------------------------------------------

def _make_deal_room(entrepreneur, business, admin, *, expires_in_days: int) -> DealRoom:
    """Helper: build a minimal OPEN DealRoom with a counterparty."""
    from django.contrib.contenttypes.models import ContentType
    from apps.smehub.linkage.models import PartnerDirectoryEntry

    partner_user = User.objects.create_user(
        email=f"partner-p4-{expires_in_days}@example.com", password="x", role=UserRole.PARTNER,
    )
    partner = PartnerDirectoryEntry.objects.create(
        organisation_name=f"Partner-P4-{expires_in_days}",
        org_type=PartnerDirectoryEntry.OrgType.NGO,
        managed_by=partner_user,
    )
    return DealRoom.objects.create(
        entrepreneur=entrepreneur,
        business=business,
        counterparty_content_type=ContentType.objects.get_for_model(PartnerDirectoryEntry),
        counterparty_object_id=str(partner.pk),
        title="Deal P4",
        expiry_date=timezone.now() + timedelta(days=expires_in_days),
        created_by=admin,
    )


def test_warn_expiring_deal_rooms_emits_for_rooms_in_window(
    entrepreneur, verified_business, admin,
):
    """A room expiring in 2 days must be warned when ``days_before=3``."""
    room = _make_deal_room(entrepreneur, verified_business, admin, expires_in_days=2)
    Notification.objects.filter(recipient=entrepreneur.user).delete()

    count = services.warn_expiring_deal_rooms(days_before=3)

    assert count == 1
    note = Notification.objects.filter(recipient=entrepreneur.user).first()
    assert note is not None
    assert "expires in" in note.message
    fresh = DealRoom.objects.get(pk=room.pk)
    assert fresh.last_expiry_warning_at is not None


def test_warn_expiring_deal_rooms_skips_rooms_outside_window(
    entrepreneur, verified_business, admin,
):
    """A room expiring in 30 days is outside the 3-day window."""
    _make_deal_room(entrepreneur, verified_business, admin, expires_in_days=30)
    count = services.warn_expiring_deal_rooms(days_before=3)
    assert count == 0


def test_warn_expiring_deal_rooms_is_idempotent(
    entrepreneur, verified_business, admin,
):
    """Running the task twice does not re-spam recipients."""
    _make_deal_room(entrepreneur, verified_business, admin, expires_in_days=2)
    services.warn_expiring_deal_rooms(days_before=3)
    second = services.warn_expiring_deal_rooms(days_before=3)
    # Idempotency-marker prevents the second pass from re-warning the same room.
    assert second == 0
