"""Tests for the ESG standard rubric (categories + criteria + anchors)."""
from __future__ import annotations

from decimal import Decimal

import pytest
from django.utils import timezone

from apps.smehub.showcasing import services
from apps.smehub.showcasing.models import (
    ChallengeRubricCategory,
    ChallengeRubricSection,
    ChallengeScoringRubric,
    InnovationChallenge,
)
from apps.smehub.showcasing.rubric_defaults import (
    ESG_RUBRIC,
    TOTAL_POINTS,
    apply_esg_rubric,
)

pytestmark = pytest.mark.django_db


def _make_rubric(admin) -> ChallengeScoringRubric:
    challenge = InnovationChallenge.objects.create(
        title="ESG Challenge",
        application_deadline=timezone.now() + timezone.timedelta(days=30),
        organiser=admin,
    )
    return ChallengeScoringRubric.objects.create(challenge=challenge)


def test_total_points_is_100():
    assert TOTAL_POINTS == 100


def test_apply_esg_rubric_builds_full_hierarchy(django_user_model):
    admin = django_user_model.objects.create_user(
        email="esg-admin@demo.local", password="x", first_name="A", last_name="D",
    )
    rubric = _make_rubric(admin)

    apply_esg_rubric(rubric)

    # 6 categories, 20 criteria.
    assert rubric.categories.count() == len(ESG_RUBRIC) == 6
    assert rubric.sections.count() == 20

    # Every criterion is out of 5 and carries five score anchors.
    for section in rubric.sections.all():
        assert section.max_marks == 5
        assert section.category is not None
        assert set(section.score_anchors.keys()) == {"1", "2", "3", "4", "5"}

    # Weights sum to exactly 1.0 → rubric is publishable.
    assert rubric.total_weight == Decimal("1.0000")
    assert rubric.is_complete is True

    # Category point totals match the document.
    points = {c.title: c.points for c in rubric.categories.all()}
    assert points["Market Potential and Problem Solving"] == 25
    assert points["Presentation Quality and Pitch Effectiveness"] == 10


def test_apply_esg_rubric_is_idempotent(django_user_model):
    admin = django_user_model.objects.create_user(
        email="esg-admin2@demo.local", password="x", first_name="A", last_name="D",
    )
    rubric = _make_rubric(admin)

    apply_esg_rubric(rubric)
    apply_esg_rubric(rubric)

    # No doubling — replacement, not append.
    assert rubric.categories.count() == 6
    assert rubric.sections.count() == 20
    assert ChallengeRubricCategory.objects.filter(rubric=rubric).count() == 6
    assert ChallengeRubricSection.objects.filter(rubric=rubric).count() == 20


def test_esg_rubric_scores_to_percentage(django_user_model):
    """A perfect 5/5 across every criterion computes to 100%."""
    admin = django_user_model.objects.create_user(
        email="esg-judge@demo.local", password="x", first_name="J", last_name="D",
    )
    rubric = _make_rubric(admin)
    apply_esg_rubric(rubric)

    from apps.smehub._shared.calls.scoring import compute_weighted_average

    sections = list(rubric.sections.all())

    class _Score:
        def __init__(self, section_id, score):
            self.section_id = section_id
            self.score = score

    perfect = [_Score(s.pk, Decimal("5")) for s in sections]
    assert compute_weighted_average(sections, perfect) == Decimal("100.00")

    midway = [_Score(s.pk, Decimal("3")) for s in sections]
    assert compute_weighted_average(sections, midway) == Decimal("60.00")


def _admin(django_user_model):
    from apps.core.permissions.roles import UserRole

    return django_user_model.objects.create_user(
        email="rubric-admin@demo.local", password="x",
        is_superuser=True, is_staff=True, role=UserRole.SYSTEM_ADMIN,
    )


def test_config_get_empty_form_has_category_choices(client, django_user_model):
    """Regression: a JS-added criterion row (rendered from ``empty_form``) must
    expose the rubric's categories in its ``category`` dropdown. Django rebuilds
    ``empty_form`` on each access, so the queryset has to arrive via
    ``form_kwargs`` — not by mutating ``formset.empty_form`` in the view."""
    from django.urls import reverse

    admin = _admin(django_user_model)
    rubric = _make_rubric(admin)
    apply_esg_rubric(rubric)
    client.force_login(admin)

    resp = client.get(
        reverse("smehub_showcasing:challenge_rubric", kwargs={"pk": rubric.challenge.pk}),
    )
    assert resp.status_code == 200
    empty_form = resp.context["formset"].empty_form
    choice_labels = [label for _, label in empty_form.fields["category"].choices]
    assert "Scientific Foundation and Innovation" in choice_labels
    assert "Presentation Quality and Pitch Effectiveness" in choice_labels


def test_apply_esg_action_provisions_rubric(client, django_user_model):
    from django.urls import reverse

    admin = _admin(django_user_model)
    rubric = _make_rubric(admin)
    client.force_login(admin)

    resp = client.post(
        reverse("smehub_showcasing:challenge_rubric", kwargs={"pk": rubric.challenge.pk}),
        {"apply_esg": "1"},
    )
    assert resp.status_code == 302
    rubric.refresh_from_db()
    assert rubric.categories.count() == 6
    assert rubric.sections.count() == 20
    assert rubric.is_complete is True


def test_apply_esg_action_blocked_when_scored(client, django_user_model):
    """PitchScore.section is PROTECT — the ESG action must not 500 when judges
    have already scored; it warns and leaves the rubric untouched."""
    from django.urls import reverse

    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 ChallengeRubricSection

    admin = _admin(django_user_model)

    # A published challenge (needs a complete rubric) with one legacy criterion.
    challenge = services.create_challenge(
        organiser=admin,
        title="Scored Challenge",
        application_deadline=timezone.now() + timezone.timedelta(days=7),
    )
    ChallengeRubricSection.objects.create(
        rubric=challenge.rubric, title="Legacy", weight=Decimal("1.0"), max_marks=10, order=0,
    )
    services.publish_challenge(challenge, by_user=admin)

    ent_user = django_user_model.objects.create_user(
        email="ent-esg@demo.local", password="x", role=UserRole.ENTREPRENEUR,
    )
    ent = EntrepreneurProfile.objects.create(user=ent_user, country="UG", organisation="Co")
    ent.verify(by_user=admin)
    ent.save()
    biz = Business.objects.create(
        entrepreneur=ent, name="ESG Biz", sector="Agriculture",
        business_stage=BusinessStage.MVP, country="UG",
    )
    biz.verify(by_user=admin)
    biz.save()
    judge = django_user_model.objects.create_user(
        email="scored-judge@demo.local", password="x", role=UserRole.JUDGE,
    )
    app = services.apply_to_challenge(
        challenge=challenge, entrepreneur=ent, business=biz, innovation_title="X",
    )
    services.submit_pitch_score(
        application=app, judge=judge,
        section=challenge.rubric.sections.first(), score=Decimal("7"),
    )

    client.force_login(admin)
    resp = client.post(
        reverse("smehub_showcasing:challenge_rubric", kwargs={"pk": challenge.pk}),
        {"apply_esg": "1"},
    )
    assert resp.status_code == 302  # graceful redirect, not a 500
    challenge.rubric.refresh_from_db()
    # Untouched: still the single legacy criterion, no ESG categories.
    assert challenge.rubric.categories.count() == 0
    assert challenge.rubric.sections.count() == 1
