"""Helpers shared by the attendance views: tenant-bound repositories, membership checks and the
few places where the legacy client still expects `is_admin`."""
from __future__ import annotations

import datetime
from types import SimpleNamespace

from rest_framework.exceptions import PermissionDenied

from application.use_cases.auth import CheckIn
from apps.workplace import services as workplace_services
from apps.access.drf import ORG_HEADER, implicit_organization
from apps.access.services import resolve_access
from apps.organizations.models import Organization, OrganizationMembership
from infrastructure.repositories import (
    DjangoAttendanceRepository,
    DjangoHolidayRepository,
    DjangoLeaveRequestRepository,
    DjangoMemberRepository,
    DjangoWorkScheduleRepository,
)
from interfaces.api.serializers import UserSerializer


def repositories_for(organization: Organization) -> SimpleNamespace:
    return SimpleNamespace(
        attendance=DjangoAttendanceRepository(organization),
        holiday=DjangoHolidayRepository(organization),
        leave=DjangoLeaveRequestRepository(organization),
        schedule=DjangoWorkScheduleRepository(organization),
        members=DjangoMemberRepository(organization),
    )


def active_membership(user, organization: Organization) -> OrganizationMembership | None:
    return OrganizationMembership.objects.filter(
        user=user, organization=organization, status=OrganizationMembership.Status.ACTIVE
    ).first()


def require_member(request) -> OrganizationMembership:
    """Self-service attendance actions need a real membership (NoTeam staff reaching in via the
    platform role may inspect data but do not have an attendance record of their own there)."""
    membership = active_membership(request.user, request.organization)
    if membership is None:
        raise PermissionDenied("You are not a member of this organization.", code="not_a_member")
    return membership


def _requested_organization(request, user) -> Organization | None:
    """Organization a *login/me* style call refers to: header if valid, else the implicit one."""
    ref = request.META.get(ORG_HEADER)
    if ref:
        ref = str(ref).strip()
        return (
            Organization.objects.filter(pk=int(ref)).first()
            if ref.isdigit()
            else Organization.objects.filter(slug=ref).first()
        )
    return implicit_organization(user)


def legacy_is_admin(request, user) -> bool:
    """The old client routes on `is_admin`. It now simply means: may view everyone's attendance
    reports in the organization this call refers to (a permission, not a stored flag)."""
    organization = _requested_organization(request, user)
    ctx = resolve_access(user, organization) if organization else None
    return bool(ctx and "attendance" in ctx.enabled_modules and ctx.has("attendance.report", "organization"))


def user_payload(request, user) -> dict:
    return UserSerializer(user, context={"is_admin": legacy_is_admin(request, user)}).data


def auto_check_in(request, user) -> None:
    """Old behaviour: signing in records today's check-in for people who are expected to check in
    (hold attendance.create, but are not report-level managers). Applies to the organization the
    login refers to; with several organizations the client must call the explicit check-in."""
    organization = _requested_organization(request, user)
    if organization is None:
        return
    ctx = resolve_access(user, organization)
    membership = active_membership(user, organization)
    if ctx is None or membership is None or "attendance" not in ctx.enabled_modules:
        return
    if ctx.has("attendance.create") and not ctx.has("attendance.report", "organization"):
        # signing in cannot prove where the person is: organizations that verify location need the explicit check-in
        if workplace_services.get_policy(organization).enabled and not workplace_services.is_exempt(organization, user, datetime.date.today()):
            return
        CheckIn(DjangoAttendanceRepository(organization)).execute(user)
