<?php

namespace App\Domain\Attendance\Services;

use App\Domain\Attendance\Enums\AttendanceStatus;
use App\Domain\Attendance\Models\AttendanceRecord;
use App\Domain\Scheduling\Models\Assignment;
use App\Domain\Training\Models\Enrollment;
use App\Domain\Training\Models\TrainingGroup;
use App\Domain\Training\Models\TrainingSession;
use Illuminate\Support\Facades\DB;

class AttendanceGenerationService
{
    public function generateForSession(TrainingSession $session): int
    {
        return DB::transaction(function () use ($session) {
            $count = 0;

            // 1. Generate for active participants (via enrollments)
            $enrollments = Enrollment::where('training_group_id', $session->training_group_id)
                ->where('status', 'active')
                ->with('participant')
                ->get();

            foreach ($enrollments as $enrollment) {
                $participant = $enrollment->participant;

                if (!$participant) {
                    continue;
                }

                $created = $this->createIfNotExists(
                    $session,
                    \App\Domain\Participant\Models\Participant::class,
                    $participant->id
                );
                if ($created) {
                    $count++;
                }
            }

            // 2. Generate for assigned staff (session-level overrides group-level)
            $assignments = $this->resolveAssignments($session);

            foreach ($assignments as $assignment) {
                $created = $this->createIfNotExists(
                    $session,
                    \App\Models\User::class,
                    $assignment->user_id
                );
                if ($created) {
                    $count++;
                }
            }

            return $count;
        });
    }

    public function generateForEnrollment(Enrollment $enrollment): int
    {
        $count = 0;
        $participant = $enrollment->participant;

        if (!$participant) {
            return 0;
        }

        // Generate for all FUTURE sessions of the group
        $futureSessions = TrainingSession::where('training_group_id', $enrollment->training_group_id)
            ->where('session_date', '>=', now()->toDateString())
            ->whereIn('status', ['scheduled', 'in_progress'])
            ->get();

        foreach ($futureSessions as $session) {
            $created = $this->createIfNotExists(
                $session,
                \App\Domain\Participant\Models\Participant::class,
                $participant->id
            );
            if ($created) {
                $count++;
            }
        }

        return $count;
    }

    public function removeForCancelledEnrollment(Enrollment $enrollment): int
    {
        // DELETE expected records for FUTURE sessions only
        return AttendanceRecord::where('subject_type', \App\Domain\Participant\Models\Participant::class)
            ->where('subject_id', $enrollment->participant_id)
            ->where('status', AttendanceStatus::Expected)
            ->whereHas('session', function ($q) use ($enrollment) {
                $q->where('training_group_id', $enrollment->training_group_id)
                    ->where('session_date', '>=', now()->toDateString());
            })
            ->delete();
    }

    public function generateUpcoming(TrainingGroup $group, int $daysAhead = 7): int
    {
        $count = 0;
        $futureSessions = TrainingSession::where('training_group_id', $group->id)
            ->where('session_date', '>=', now()->toDateString())
            ->where('session_date', '<=', now()->addDays($daysAhead)->toDateString())
            ->whereIn('status', ['scheduled'])
            ->get();

        foreach ($futureSessions as $session) {
            $count += $this->generateForSession($session);
        }

        return $count;
    }

    private function resolveAssignments(TrainingSession $session): \Illuminate\Support\Collection
    {
        // Session-level assignments override group-level
        $sessionAssignments = Assignment::where('assignable_type', TrainingSession::class)
            ->where('assignable_id', $session->id)
            ->where('status', 'active')
            ->get();

        if ($sessionAssignments->isNotEmpty()) {
            return $sessionAssignments;
        }

        // Fall back to group-level
        return Assignment::where('assignable_type', TrainingGroup::class)
            ->where('assignable_id', $session->training_group_id)
            ->where('status', 'active')
            ->get();
    }

    private function createIfNotExists(TrainingSession $session, string $subjectType, int $subjectId): bool
    {
        $exists = AttendanceRecord::where('training_session_id', $session->id)
            ->where('subject_type', $subjectType)
            ->where('subject_id', $subjectId)
            ->exists();

        if ($exists) {
            return false;
        }

        AttendanceRecord::create([
            'academy_id' => $session->academy_id,
            // Explicit for the same reason the session's own branch is: this runs from
            // a queued listener, where BranchContext::branchIdForStamping() returns
            // null. A branchless register is one the coach cannot open — which is what
            // `attendance:backfill` exists at 02:30 to repair, using this same
            // session -> group attribution.
            'branch_id' => $session->branch_id ?? $session->group?->branch_id,
            'training_session_id' => $session->id,
            'subject_type' => $subjectType,
            'subject_id' => $subjectId,
            'status' => AttendanceStatus::Expected->value,
            'is_auto_generated' => true,
        ]);

        return true;
    }
}
