"""Shared fixtures for v1 API regression tests.

Builds a "two programmes, two grants_managers" world used by the IDOR /
programme-scope tests so we can assert that a manager assigned to programme A
cannot see programme B's rows.
"""
from __future__ import annotations

from datetime import timedelta
from decimal import Decimal

import pytest
from django.contrib.auth import get_user_model
from django.utils import timezone
from rest_framework.test import APIClient

from apps.core.permissions.roles import UserRole
from apps.rims.grants.models import (
    Application,
    Award,
    FundingRecord,
    GrantCall,
    InterviewSchedule,
    VerificationRecord,
)

User = get_user_model()


def _user(email: str, role: UserRole | str) -> "User":
    role_value = role.value if hasattr(role, "value") else role
    user = User.objects.create_user(email=email, password="pw-api-1234")
    user.role = role_value
    user.save(update_fields=["role"])
    return user


@pytest.fixture
def api_client() -> APIClient:
    return APIClient()


@pytest.fixture
def admin_user(db):
    u = _user("admin.api@example.com", UserRole.ADMIN)
    return u


@pytest.fixture
def applicant_a(db):
    return _user("applicant.a@example.com", UserRole.APPLICANT)


@pytest.fixture
def applicant_b(db):
    return _user("applicant.b@example.com", UserRole.APPLICANT)


@pytest.fixture
def grants_manager_a(db):
    return _user("gm.a@example.com", UserRole.GRANTS_MANAGER)


@pytest.fixture
def grants_manager_b(db):
    return _user("gm.b@example.com", UserRole.GRANTS_MANAGER)


@pytest.fixture
def finance_officer_a(db):
    return _user("fo.a@example.com", UserRole.FINANCE_OFFICER)


@pytest.fixture
def mel_officer(db):
    return _user("mel.officer@example.com", UserRole.MEL_OFFICER)


@pytest.fixture
def learner_user(db):
    return _user("learner.api@example.com", UserRole.LEARNER)


_FR_COUNTER = {"n": 0}


def _funding_record(
    *, title: str, manager, finance_officer=None, program_lead=None
) -> FundingRecord:
    today = timezone.now().date()
    _FR_COUNTER["n"] += 1
    return FundingRecord.objects.create(
        public_grant_id=f"TEST-FR-{_FR_COUNTER['n']:04d}",
        title=title,
        amount=Decimal("100000.00"),
        currency="USD",
        start_date=today,
        end_date=today + timedelta(days=365),
        grant_manager=manager,
        finance_officer=finance_officer,
        program_lead=program_lead,
        status=FundingRecord.Status.APPROVED,
    )


def _grant_call(funding_record: FundingRecord, *, slug: str, title: str) -> GrantCall:
    return GrantCall.objects.create(
        title=title,
        slug=slug,
        funding_record=funding_record,
        opens_at=timezone.now() - timedelta(days=1),
        closes_at=timezone.now() + timedelta(days=30),
        status=GrantCall.Status.PUBLISHED,
    )


def _application(call: GrantCall, applicant) -> Application:
    return Application.objects.create(
        call=call,
        applicant=applicant,
        status=Application.Status.SUBMITTED,
        submitted_at=timezone.now(),
    )


@pytest.fixture
def programme_a(db, grants_manager_a, finance_officer_a):
    """Programme A — managed by grants_manager_a + finance_officer_a."""
    fr = _funding_record(
        title="Programme A",
        manager=grants_manager_a,
        finance_officer=finance_officer_a,
    )
    call = _grant_call(fr, slug="prog-a-call-1", title="Programme A — Call 1")
    return {"funding_record": fr, "call": call}


@pytest.fixture
def programme_b(db, grants_manager_b):
    """Programme B — managed by grants_manager_b only."""
    fr = _funding_record(title="Programme B", manager=grants_manager_b)
    call = _grant_call(fr, slug="prog-b-call-1", title="Programme B — Call 1")
    return {"funding_record": fr, "call": call}


@pytest.fixture
def application_a(db, programme_a, applicant_a) -> Application:
    return _application(programme_a["call"], applicant_a)


@pytest.fixture
def application_b(db, programme_b, applicant_b) -> Application:
    return _application(programme_b["call"], applicant_b)


@pytest.fixture
def interview_a(db, application_a) -> InterviewSchedule:
    return InterviewSchedule.objects.create(
        application=application_a,
        scheduled_for=timezone.now() + timedelta(days=7),
    )


@pytest.fixture
def interview_b(db, application_b) -> InterviewSchedule:
    return InterviewSchedule.objects.create(
        application=application_b,
        scheduled_for=timezone.now() + timedelta(days=7),
    )


@pytest.fixture
def verification_a(db, application_a) -> VerificationRecord:
    return VerificationRecord.objects.create(application=application_a)


@pytest.fixture
def verification_b(db, application_b) -> VerificationRecord:
    return VerificationRecord.objects.create(application=application_b)


@pytest.fixture
def award_a(db, application_a) -> Award:
    return Award.objects.create(
        application=application_a,
        amount=Decimal("50000.00"),
        currency="USD",
        project_end_date=(timezone.now() + timedelta(days=180)).date(),
    )


@pytest.fixture
def award_b(db, application_b) -> Award:
    return Award.objects.create(
        application=application_b,
        amount=Decimal("50000.00"),
        currency="USD",
        project_end_date=(timezone.now() + timedelta(days=180)).date(),
    )
