"""Phase 8 — Employment snapshot capture for FRSME-MEI013 jobs_created.

The indicator was a placeholder until Phase 8: BusinessBaseline is a
one-shot row, so without a follow-up mechanism the system had no way to
record post-incubation employment growth. This module + test suite closes
that gap.
"""
from __future__ import annotations

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

from apps.core.permissions.roles import UserRole
from apps.mel.tracking.models import (
    EmploymentSnapshot,
    SMEHubTrackingRecord,
)
from apps.mel.tracking.smehub_employment import (
    compute_jobs_created,
    compute_total_jobs_created,
    record_employment_snapshot,
)
from apps.smehub.onboarding import signals as onb_signals
from apps.smehub.onboarding.models import (
    Business,
    BusinessBaseline,
    BusinessStage,
    EntrepreneurProfile,
)

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


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

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


@pytest.fixture
def mel_officer():
    return User.objects.create_user(
        email="mel-emp8@example.com", password="x", role=UserRole.MEL_OFFICER,
    )


def _build_record(admin, *, fte: int = 2, pte: int = 3, suffix: str = "0"):
    user = User.objects.create_user(
        email=f"ent-emp8-{suffix}@example.com", password="x", role=UserRole.ENTREPRENEUR,
    )
    profile = EntrepreneurProfile.objects.create(user=user, country="UG")
    profile.verify(by_user=admin)
    profile.save()
    biz = Business.objects.create(
        entrepreneur=profile, name=f"Biz Emp8 {suffix}",
        sector="Agriculture", business_stage=BusinessStage.MVP, country="UG",
    )
    baseline = BusinessBaseline.objects.create(
        business=biz, business_stage_at_entry=BusinessStage.MVP,
        fte_count=fte, pte_count=pte,
    )
    biz.verify(by_user=admin)
    biz.save()
    onb_signals.smehub_baseline_initialised.send(
        sender=BusinessBaseline, business=biz, baseline=baseline, entrepreneur=profile,
    )
    return SMEHubTrackingRecord.objects.get(entrepreneur=profile)


@pytest.fixture
def record(admin):
    return _build_record(admin, fte=2, pte=3)


# ---------------------------------------------------------------------------
# Service layer
# ---------------------------------------------------------------------------

def test_record_employment_snapshot_creates_row_and_audit(record, mel_officer):
    from apps.core.audit.models import AuditLog

    snap = record_employment_snapshot(
        record, fte_count=5, pte_count=4, by_user=mel_officer, note="Post-Q2 review",
    )
    assert snap.fte_count == 5
    assert snap.pte_count == 4
    assert snap.note == "Post-Q2 review"
    assert snap.captured_by == mel_officer
    assert AuditLog.objects.filter(
        target_app="mel_tracking", target_model="employmentsnapshot",
    ).exists()


def test_compute_jobs_created_uses_latest_snapshot_minus_baseline(record, mel_officer):
    # Baseline is FTE=2 + PTE=3 = 5
    record_employment_snapshot(record, fte_count=5, pte_count=4, by_user=mel_officer)
    # Latest is 5+4=9; baseline=5; delta=4
    assert compute_jobs_created(record) == 4


def test_compute_jobs_created_uses_only_latest_when_multiple(record, mel_officer):
    record_employment_snapshot(record, fte_count=4, pte_count=3, by_user=mel_officer)
    record_employment_snapshot(record, fte_count=8, pte_count=5, by_user=mel_officer)
    # Latest=13; baseline=5; delta=8
    assert compute_jobs_created(record) == 8


def test_compute_jobs_created_clamps_at_zero_when_shrinkage(record, mel_officer):
    # Layoffs: latest 1+1=2 vs baseline 5 → delta=-3 → clamped to 0
    record_employment_snapshot(record, fte_count=1, pte_count=1, by_user=mel_officer)
    assert compute_jobs_created(record) == 0


def test_compute_jobs_created_returns_zero_with_no_snapshot(record):
    assert compute_jobs_created(record) == 0


def test_compute_total_jobs_created_aggregates_across_records(admin, mel_officer):
    r1 = _build_record(admin, fte=2, pte=3, suffix="1")
    r2 = _build_record(admin, fte=1, pte=1, suffix="2")
    record_employment_snapshot(r1, fte_count=5, pte_count=4, by_user=mel_officer)  # delta 4
    record_employment_snapshot(r2, fte_count=3, pte_count=2, by_user=mel_officer)  # delta 3
    assert compute_total_jobs_created() == 7


def test_compute_total_jobs_created_excludes_negative_records(admin, mel_officer):
    r1 = _build_record(admin, fte=2, pte=3, suffix="3")  # baseline=5
    r2 = _build_record(admin, fte=5, pte=5, suffix="4")  # baseline=10
    record_employment_snapshot(r1, fte_count=6, pte_count=4, by_user=mel_officer)  # latest=10, +5
    record_employment_snapshot(r2, fte_count=2, pte_count=2, by_user=mel_officer)  # latest=4, -6 clamped 0
    assert compute_total_jobs_created() == 5


# ---------------------------------------------------------------------------
# View layer
# ---------------------------------------------------------------------------

def _record_url(record):
    return reverse("mel_tracking:smehub_record_employment", kwargs={"pk": record.pk})


def _detail_url(record):
    return reverse("mel_tracking:smehub_tracking_detail", kwargs={"pk": record.pk})


def test_record_view_blocks_anonymous(client, record):
    resp = client.post(_record_url(record), {"fte_count": 1, "pte_count": 0})
    assert resp.status_code in (302, 403)
    assert not EmploymentSnapshot.objects.exists()


def test_record_view_blocks_entrepreneur(client, record):
    client.force_login(record.entrepreneur.user)
    resp = client.post(_record_url(record), {"fte_count": 1, "pte_count": 0})
    assert resp.status_code == 403
    assert not EmploymentSnapshot.objects.exists()


def test_record_view_creates_snapshot_for_mel_officer(client, record, mel_officer):
    client.force_login(mel_officer)
    resp = client.post(_record_url(record), {"fte_count": 6, "pte_count": 2, "note": "Q3"})
    assert resp.status_code == 302
    assert EmploymentSnapshot.objects.filter(
        tracking_record=record, fte_count=6, pte_count=2, captured_by=mel_officer,
    ).exists()


def test_record_view_rejects_negative_counts(client, record, mel_officer):
    client.force_login(mel_officer)
    resp = client.post(_record_url(record), {"fte_count": -1, "pte_count": 0})
    assert resp.status_code == 302
    assert not EmploymentSnapshot.objects.exists()


def test_detail_view_renders_form_and_jobs_tile(client, record, mel_officer):
    client.force_login(mel_officer)
    resp = client.get(_detail_url(record))
    assert resp.status_code == 200
    assert b"Employment updates" in resp.content
    assert b"Jobs created" in resp.content
    assert b"Record snapshot" in resp.content


def test_detail_view_renders_history(client, record, mel_officer):
    record_employment_snapshot(record, fte_count=4, pte_count=3, by_user=mel_officer, note="Q1")
    client.force_login(mel_officer)
    resp = client.get(_detail_url(record))
    assert resp.status_code == 200
    assert b"Q1" in resp.content


# ---------------------------------------------------------------------------
# Portfolio stats
# ---------------------------------------------------------------------------

def test_portfolio_stats_includes_jobs_created(admin, mel_officer):
    r = _build_record(admin, fte=2, pte=2, suffix="ps")
    record_employment_snapshot(r, fte_count=5, pte_count=3, by_user=mel_officer)
    from apps.mel.tracking.views import _portfolio_stats

    stats = _portfolio_stats()
    assert "jobs_created" in stats
    assert stats["jobs_created"] == 4
