"""Phase 7 SP2 incubation audit fixes.

Coverage:
* ``Business.programme_of_origin`` is set on cohort confirmation and not
  overwritten by a subsequent cohort acceptance — preserving the source
  programme for SP6 cross-system reporting.
* FRSME-INC024 — confirm_cohort schedules the REP auto-enrol Celery task
  on commit when the programme has linked courses, and skips dispatch
  when the programme has no course links.
"""
from __future__ import annotations

from datetime import timedelta
from decimal import Decimal
from unittest.mock import patch

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

from apps.core.permissions.roles import UserRole
from apps.smehub.incubation import services
from apps.smehub.incubation.models import Application, Programme
from apps.smehub.onboarding.models import (
    Business,
    BusinessBaseline,
    BusinessStage,
    EntrepreneurProfile,
    RevenueRange,
)

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


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

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


@pytest.fixture
def officer():
    return User.objects.create_user(
        email="po-p7@example.com", password="x", role=UserRole.PROGRAMME_OFFICER,
    )


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


@pytest.fixture
def verified_business(entrepreneur, admin):
    biz = Business.objects.create(
        entrepreneur=entrepreneur,
        name="P7 Biz",
        sector="Agriculture",
        business_stage=BusinessStage.MVP,
        country="UG",
    )
    baseline = BusinessBaseline.objects.create(
        business=biz, business_stage_at_entry=BusinessStage.MVP,
        fte_count=1, pte_count=0,
        revenue_range=RevenueRange.UNDER_1K,
        primary_target_market="Local", geographic_reach="UG",
    )
    biz.verify(by_user=admin)
    biz.save()
    from apps.smehub.onboarding import signals as onb_signals

    onb_signals.smehub_baseline_initialised.send(
        sender=BusinessBaseline, business=biz, baseline=baseline, entrepreneur=entrepreneur,
    )
    return biz


@pytest.fixture
def programme_open(officer):
    p = services.create_programme(
        officer=officer,
        title="P7 Programme",
        slug="p7-programme",
        target_sectors=["Agriculture"],
        target_stages=["mvp"],
        cohort_size=5,
        duration_weeks=10,
        application_deadline=timezone.now() + timedelta(days=30),
    )
    services.add_rubric_section(p, title="A", weight=Decimal("0.5"), max_marks=10)
    services.add_rubric_section(p, title="B", weight=Decimal("0.5"), max_marks=10)
    p.judges.set([User.objects.create_user(email="p7fix-roster-judge@example.com", password="x", role=UserRole.JUDGE)])
    services.publish_programme(p, by_user=officer)
    return Programme.objects.get(pk=p.pk)


def _score_app(application, programme, officer):
    judges = [
        User.objects.create_user(
            email=f"j-p7-{application.pk}-{i}@example.com",
            password="x", role=UserRole.JUDGE,
        )
        for i in range(2)
    ]
    services.assign_judges(application, judges=judges, by_user=officer)
    sections = list(programme.rubric.sections.all())
    for judge in judges:
        services.submit_score(
            application, judge=judge,
            section_scores={s.pk: 8 for s in sections},
        )


def _admit(programme, business, entrepreneur, officer, *, name="Cohort"):
    app = services.start_application(
        entrepreneur=entrepreneur, business=business, programme=programme,
    )
    app.business_description = "Test Biz supplies inputs to smallholder farmers."
    app.problem_statement = "Smallholders lack reliable access to quality inputs."
    app.save()
    services.submit_application(app)
    _score_app(app, programme, officer)
    services.confirm_cohort(
        programme,
        accepted_application_ids=[app.pk],
        name=name,
        start_date=timezone.now().date(),
        end_date=timezone.now().date() + timedelta(days=84),
        by_user=officer,
    )
    return app


# ---------------------------------------------------------------------------
# Business.programme_of_origin
# ---------------------------------------------------------------------------

def test_confirm_cohort_sets_programme_of_origin(
    programme_open, verified_business, entrepreneur, officer,
):
    assert verified_business.programme_of_origin_id is None
    _admit(programme_open, verified_business, entrepreneur, officer)
    fresh = Business.objects.get(pk=verified_business.pk)
    assert fresh.programme_of_origin_id == programme_open.pk


def test_programme_of_origin_is_not_overwritten_by_second_cohort(
    programme_open, verified_business, entrepreneur, officer,
):
    """A business reaccepted into a later programme keeps the FIRST programme
    as its origin — SP6 reporting attributes outcomes to source programme."""
    _admit(programme_open, verified_business, entrepreneur, officer, name="C1")
    fresh = Business.objects.get(pk=verified_business.pk)
    assert fresh.programme_of_origin_id == programme_open.pk

    second = services.create_programme(
        officer=officer,
        title="P7 Second",
        slug="p7-second",
        target_sectors=["Agriculture"],
        target_stages=["mvp"],
        cohort_size=5,
        duration_weeks=10,
        application_deadline=timezone.now() + timedelta(days=60),
    )
    services.add_rubric_section(second, title="A", weight=Decimal("1"), max_marks=10)
    second.judges.set([User.objects.create_user(email="p7fix-second-judge@example.com", password="x", role=UserRole.JUDGE)])
    services.publish_programme(second, by_user=officer)
    second = Programme.objects.get(pk=second.pk)
    _admit(second, fresh, entrepreneur, officer, name="C2")

    after = Business.objects.get(pk=verified_business.pk)
    assert after.programme_of_origin_id == programme_open.pk  # unchanged


# ---------------------------------------------------------------------------
# FRSME-INC024 — REP auto-enrol fan-out
# ---------------------------------------------------------------------------

def test_confirm_cohort_does_not_dispatch_enrol_task_without_course_links(
    programme_open, verified_business, entrepreneur, officer,
):
    """Cohort with no linked REP courses must not enqueue the enrolment task."""
    from django.test import TestCase

    with patch(
        "apps.smehub.incubation.tasks.enrol_cohort_in_courses_task.delay"
    ) as task:
        with TestCase.captureOnCommitCallbacks(execute=True):
            _admit(programme_open, verified_business, entrepreneur, officer)
    task.assert_not_called()


def test_confirm_cohort_dispatches_enrol_task_when_courses_linked(
    programme_open, verified_business, entrepreneur, officer,
):
    """When the programme has at least one linked Moodle course, confirm_cohort
    must schedule the auto-enrol Celery task post-commit."""
    from django.test import TestCase

    services.link_programme_course(
        programme_open, moodle_course_id="p7-101", course_title="Moodle Course P7",
    )

    with patch(
        "apps.smehub.incubation.tasks.enrol_cohort_in_courses_task.delay"
    ) as task:
        with TestCase.captureOnCommitCallbacks(execute=True):
            _admit(programme_open, verified_business, entrepreneur, officer)
    task.assert_called_once()
    assert task.call_args.args[0]  # cohort id was passed
