"""Tests for SME-Hub Innovation lane (Phase 6.1).

Coverage:
* Draft → Submit → UnderVetting → (Approve | Reject | Revise) → Resubmit → Onboard
* Evaluator assignment + scoring + recommendation aggregation
* RBAC: only entrepreneur owners can draft/submit; only admins finalize.
* MEL receiver fires on approve/publish.
"""
from __future__ import annotations

import pytest
from django.contrib.auth import get_user_model

from apps.core.permissions.roles import UserRole
from apps.smehub.onboarding import services
from apps.smehub.onboarding.models import (
    Business,
    BusinessStage,
    EntrepreneurProfile,
    EvaluatorAssignment,
    Innovation,
    VettingRecord,
    VettingScore,
)

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 evaluator():
    return User.objects.create_user(
        email="eval@example.com", password="x", role=UserRole.JUDGE,
    )


@pytest.fixture
def second_evaluator():
    return User.objects.create_user(
        email="eval2@example.com", password="x", role=UserRole.JUDGE,
    )


def _draft_innovation(business, **overrides) -> Innovation:
    payload = dict(
        title="SoilSensor",
        description="Cheap soil moisture sensor for smallholders.",
        sector="Agriculture",
        country="UG",
        stage=Innovation.Stage.PROTOTYPE,
        team_size=3,
    )
    payload.update(overrides)
    return services.create_innovation_draft(
        business=business, by_user=business.entrepreneur.user, **payload,
    )


# ---------------------------------------------------------------------------
# Drafting
# ---------------------------------------------------------------------------

def test_create_innovation_blocked_for_unverified_business(entrepreneur):
    biz = Business.objects.create(
        entrepreneur=entrepreneur,
        name="Pending Co",
        sector="Agriculture",
        business_stage=BusinessStage.MVP,
        country="UG",
    )
    with pytest.raises(services.InnovationStateError):
        services.create_innovation_draft(
            business=biz, by_user=entrepreneur.user, title="X", sector="Agriculture",
        )


def test_create_innovation_persists_with_id(verified_business):
    inv = _draft_innovation(verified_business)
    assert inv.pk is not None
    assert inv.innovation_id.startswith("INN-")
    assert inv.lifecycle_status == Innovation.Status.DRAFT


def test_update_innovation_blocked_outside_draft_states(verified_business, admin):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    inv = Innovation.objects.get(pk=inv.pk)
    services.assign_evaluator(inv, evaluator=admin, by_user=admin)
    inv = Innovation.objects.get(pk=inv.pk)
    assert inv.lifecycle_status == Innovation.Status.UNDER_VETTING
    with pytest.raises(services.InnovationStateError):
        services.update_innovation_draft(
            inv, by_user=verified_business.entrepreneur.user, title="X",
        )


# ---------------------------------------------------------------------------
# Submit / Vetting
# ---------------------------------------------------------------------------

def test_submit_innovation_transitions_to_submitted(verified_business):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    inv = Innovation.objects.get(pk=inv.pk)
    assert inv.lifecycle_status == Innovation.Status.SUBMITTED
    assert inv.submitted_at is not None


def test_assign_evaluator_first_time_advances_to_under_vetting(
    verified_business, admin, evaluator,
):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=evaluator, by_user=admin)
    inv = Innovation.objects.get(pk=inv.pk)
    assert inv.lifecycle_status == Innovation.Status.UNDER_VETTING
    assert inv.vetting_started_at is not None
    assert EvaluatorAssignment.objects.filter(innovation=inv, evaluator=evaluator).exists()
    assert VettingRecord.objects.filter(innovation=inv).exists()


def test_assign_evaluator_second_time_keeps_under_vetting(
    verified_business, admin, evaluator, second_evaluator,
):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=evaluator, by_user=admin)
    services.assign_evaluator(inv, evaluator=second_evaluator, by_user=admin)
    inv = Innovation.objects.get(pk=inv.pk)
    assert inv.lifecycle_status == Innovation.Status.UNDER_VETTING
    # Still exactly one open vetting round.
    assert VettingRecord.objects.filter(innovation=inv).count() == 1


def test_score_creates_score_and_marks_assignment_in_progress(
    verified_business, admin, evaluator,
):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=evaluator, by_user=admin)
    services.score_vetting_dimension(
        innovation=inv,
        evaluator=evaluator,
        dimension=VettingScore.Dimension.MARKET_POTENTIAL,
        score=4,
        comments="Solid demand from coops.",
    )
    score = VettingScore.objects.get(evaluator=evaluator, dimension="market_potential")
    assert score.score == 4
    assignment = EvaluatorAssignment.objects.get(innovation=inv, evaluator=evaluator)
    assert assignment.status == EvaluatorAssignment.Status.IN_PROGRESS


def test_score_blocked_when_evaluator_not_assigned(
    verified_business, admin, second_evaluator,
):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=admin, by_user=admin)
    with pytest.raises(services.InnovationStateError):
        services.score_vetting_dimension(
            innovation=inv,
            evaluator=second_evaluator,
            dimension=VettingScore.Dimension.TEAM_CAPACITY,
            score=3,
        )


def test_recommendation_completes_assignment(verified_business, admin, evaluator):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=evaluator, by_user=admin)
    services.submit_vetting_recommendation(
        innovation=inv,
        evaluator=evaluator,
        recommendation=VettingRecord.Recommendation.APPROVE,
        summary="Strong team, clear demand.",
    )
    assignment = EvaluatorAssignment.objects.get(innovation=inv, evaluator=evaluator)
    assert assignment.status == EvaluatorAssignment.Status.COMPLETED
    record = VettingRecord.objects.get(innovation=inv)
    assert record.recommendation == VettingRecord.Recommendation.APPROVE


# ---------------------------------------------------------------------------
# Admin finalization
# ---------------------------------------------------------------------------

def test_vet_application_approves(verified_business, admin, evaluator):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=evaluator, by_user=admin)
    services.vet_application(inv, by_user=admin, decision="approve", feedback="Great fit")
    inv = Innovation.objects.get(pk=inv.pk)
    assert inv.lifecycle_status == Innovation.Status.APPROVED
    assert inv.decided_at is not None
    record = VettingRecord.objects.get(innovation=inv)
    assert record.closed_at is not None
    assert record.recommendation == VettingRecord.Recommendation.APPROVE


def test_vet_application_request_revisions_returns_to_revisions_state(
    verified_business, admin, evaluator,
):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=evaluator, by_user=admin)
    services.vet_application(
        inv, by_user=admin, decision="revise", feedback="Add unit economics.",
    )
    inv = Innovation.objects.get(pk=inv.pk)
    assert inv.lifecycle_status == Innovation.Status.REVISIONS_REQUESTED
    assert inv.feedback_to_innovator == "Add unit economics."


def test_vet_application_reject_requires_reason(verified_business, admin, evaluator):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=evaluator, by_user=admin)
    with pytest.raises(ValueError):
        services.vet_application(inv, by_user=admin, decision="reject", reason="")


def test_resubmit_after_revisions(verified_business, admin, evaluator):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=evaluator, by_user=admin)
    services.vet_application(inv, by_user=admin, decision="revise", feedback="Tighten team.")
    inv = Innovation.objects.get(pk=inv.pk)
    services.update_innovation_draft(
        inv, by_user=verified_business.entrepreneur.user, team_summary="3 founders + 2 advisors",
    )
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    inv = Innovation.objects.get(pk=inv.pk)
    assert inv.lifecycle_status == Innovation.Status.SUBMITTED
    assert inv.feedback_to_innovator == ""  # cleared on resubmit


def test_onboard_publishes_innovation(verified_business, admin, evaluator):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=evaluator, by_user=admin)
    services.vet_application(inv, by_user=admin, decision="approve")
    services.onboard(inv, by_user=admin)
    inv = Innovation.objects.get(pk=inv.pk)
    assert inv.is_public is True
    assert inv.published_at is not None


def test_onboard_blocked_before_approval(verified_business, admin):
    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    with pytest.raises(ValueError):
        services.onboard(inv, by_user=admin)


def test_withdraw_innovation_terminates(verified_business):
    inv = _draft_innovation(verified_business)
    services.withdraw_innovation(
        inv, by_user=verified_business.entrepreneur.user, reason="Pivoting.",
    )
    inv = Innovation.objects.get(pk=inv.pk)
    assert inv.lifecycle_status == Innovation.Status.WITHDRAWN
    assert inv.withdrawn_reason == "Pivoting."


# ---------------------------------------------------------------------------
# MEL handoff
# ---------------------------------------------------------------------------

def test_innovation_approved_emits_mel_event(verified_business, admin, evaluator):
    from apps.mel.tracking.models import TrackingMilestone

    inv = _draft_innovation(verified_business)
    services.submit_innovation(inv, by_user=verified_business.entrepreneur.user)
    services.assign_evaluator(inv, evaluator=evaluator, by_user=admin)
    services.vet_application(inv, by_user=admin, decision="approve")
    services.onboard(inv, by_user=admin)
    milestones = list(
        TrackingMilestone.objects.filter(
            event_type__in=["smehub_innovation_approved", "smehub_innovation_published"],
            source_id=str(inv.pk),
        )
    )
    event_types = {m.event_type for m in milestones}
    assert "smehub_innovation_approved" in event_types
    assert "smehub_innovation_published" in event_types
