<?php

namespace App\Livewire\Reports;

use App\Domain\Attendance\Enums\AttendanceStatus;
use App\Domain\Attendance\Models\AttendanceRecord;
use App\Domain\Participant\Models\Participant;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Training\Models\TrainingGroup;
use App\Domain\Training\Models\TrainingProgram;
use App\Domain\Training\Models\TrainingSession;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Attributes\Url;
use Livewire\Component;
use Symfony\Component\HttpFoundation\StreamedResponse;

#[Layout('layouts.app')]
#[Title('تقرير الحضور')]
class AttendanceReport extends Component
{
    use UsesBranchScope;

    #[Url]
    public string $groupFilter = '';

    #[Url]
    public string $programFilter = '';

    #[Url]
    public string $dateFrom = '';

    #[Url]
    public string $dateTo = '';

    public function mount(): void
    {
        $this->authorize('reports.attendance');

        if (! $this->dateFrom) {
            $this->dateFrom = now()->startOfMonth()->toDateString();
        }
        if (! $this->dateTo) {
            $this->dateTo = now()->toDateString();
        }
    }

    public function updatedGroupFilter(): void
    {
        // Reset program filter when group changes to avoid conflicts
    }

    public function updatedProgramFilter(): void
    {
        // Reset group filter when program changes
        $this->groupFilter = '';
    }

    public function getParticipantStats(): Collection
    {
        $branchId = $this->getActiveBranchId();

        $sessionIds = $this->getFilteredSessionIds($branchId);

        if ($sessionIds->isEmpty()) {
            return collect();
        }

        $stats = AttendanceRecord::query()
            ->whereIn('training_session_id', $sessionIds)
            ->where('subject_type', 'App\\Domain\\Participant\\Models\\Participant')
            ->select(
                'subject_id',
                DB::raw("COUNT(*) as total_sessions"),
                DB::raw("SUM(CASE WHEN status = 'present' THEN 1 ELSE 0 END) as present_count"),
                DB::raw("SUM(CASE WHEN status = 'late' THEN 1 ELSE 0 END) as late_count"),
                DB::raw("SUM(CASE WHEN status = 'partial' THEN 1 ELSE 0 END) as partial_count"),
                DB::raw("SUM(CASE WHEN status IN ('absent', 'no_show') THEN 1 ELSE 0 END) as absent_count"),
                DB::raw("SUM(CASE WHEN status = 'excused' THEN 1 ELSE 0 END) as excused_count"),
                DB::raw("SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) as cancelled_count"),
                DB::raw("SUM(CASE WHEN status = 'exempt' THEN 1 ELSE 0 END) as exempt_count"),
            )
            ->groupBy('subject_id')
            ->get();

        $participantIds = $stats->pluck('subject_id');
        $participants = Participant::with('person')
            ->whereIn('id', $participantIds)
            ->get()
            ->keyBy('id');

        return $stats->map(function ($stat) use ($participants) {
            $participant = $participants->get($stat->subject_id);
            $denominator = $stat->total_sessions - $stat->cancelled_count - $stat->exempt_count;
            $positiveCount = $stat->present_count + $stat->late_count + $stat->partial_count;
            $rate = $denominator > 0 ? round(($positiveCount / $denominator) * 100, 1) : 0;

            return (object) [
                'participant_id' => $stat->subject_id,
                'name' => $participant?->person?->name_ar ?? $participant?->person?->name ?? __('غير معروف'),
                'participant_number' => $participant?->participant_number,
                'total_sessions' => $stat->total_sessions,
                'present_count' => $stat->present_count,
                'late_count' => $stat->late_count,
                'partial_count' => $stat->partial_count,
                'absent_count' => $stat->absent_count,
                'excused_count' => $stat->excused_count,
                'cancelled_count' => $stat->cancelled_count,
                'exempt_count' => $stat->exempt_count,
                'rate' => $rate,
            ];
        })->sortByDesc('rate')->values();
    }

    public function getGroupSummary(): object
    {
        $branchId = $this->getActiveBranchId();
        $sessionIds = $this->getFilteredSessionIds($branchId);

        if ($sessionIds->isEmpty()) {
            return (object) [
                'overall_rate' => 0,
                'total_sessions' => 0,
                'avg_present_per_session' => 0,
                'best_day' => null,
                'worst_day' => null,
            ];
        }

        $totalRecords = AttendanceRecord::whereIn('training_session_id', $sessionIds)
            ->where('subject_type', 'App\\Domain\\Participant\\Models\\Participant')
            ->count();

        $excludedCount = AttendanceRecord::whereIn('training_session_id', $sessionIds)
            ->where('subject_type', 'App\\Domain\\Participant\\Models\\Participant')
            ->whereIn('status', [AttendanceStatus::Cancelled, AttendanceStatus::Exempt])
            ->count();

        $positiveCount = AttendanceRecord::whereIn('training_session_id', $sessionIds)
            ->where('subject_type', 'App\\Domain\\Participant\\Models\\Participant')
            ->whereIn('status', [AttendanceStatus::Present, AttendanceStatus::Late, AttendanceStatus::Partial])
            ->count();

        $denominator = $totalRecords - $excludedCount;
        $overallRate = $denominator > 0 ? round(($positiveCount / $denominator) * 100, 1) : 0;

        $totalSessions = $sessionIds->count();
        $avgPresent = $totalSessions > 0 ? round($positiveCount / $totalSessions, 1) : 0;

        // Best and worst attendance days
        $dailyRates = AttendanceRecord::query()
            ->whereIn('attendance_records.training_session_id', $sessionIds)
            ->where('attendance_records.subject_type', 'App\\Domain\\Participant\\Models\\Participant')
            ->join('training_sessions', 'training_sessions.id', '=', 'attendance_records.training_session_id')
            ->select(
                'training_sessions.session_date',
                DB::raw("COUNT(*) as total"),
                DB::raw("SUM(CASE WHEN attendance_records.status IN ('present', 'late', 'partial') THEN 1 ELSE 0 END) as positive"),
                DB::raw("SUM(CASE WHEN attendance_records.status IN ('cancelled', 'exempt') THEN 1 ELSE 0 END) as excluded"),
            )
            ->groupBy('training_sessions.session_date')
            ->get()
            ->map(function ($day) {
                $denom = $day->total - $day->excluded;
                $day->rate = $denom > 0 ? round(($day->positive / $denom) * 100, 1) : 0;

                return $day;
            });

        $bestDay = $dailyRates->sortByDesc('rate')->first();
        $worstDay = $dailyRates->sortBy('rate')->first();

        return (object) [
            'overall_rate' => $overallRate,
            'total_sessions' => $totalSessions,
            'avg_present_per_session' => $avgPresent,
            'best_day' => $bestDay?->session_date,
            'best_day_rate' => $bestDay?->rate ?? 0,
            'worst_day' => $worstDay?->session_date,
            'worst_day_rate' => $worstDay?->rate ?? 0,
        ];
    }

    public function export(): StreamedResponse
    {
        $this->authorize('reports.attendance');

        $stats = $this->getParticipantStats();

        $filename = 'attendance-report-' . now()->format('Y-m-d');

        return response()->streamDownload(function () use ($stats) {
            $handle = fopen('php://output', 'w');

            // UTF-8 BOM for Excel Arabic support
            fprintf($handle, chr(0xEF) . chr(0xBB) . chr(0xBF));

            // Headers
            fputcsv($handle, [
                __('رقم المشترك'),
                __('الاسم'),
                __('إجمالي الحصص'),
                __('حاضر'),
                __('متأخر'),
                __('جزئي'),
                __('غائب'),
                __('معذور'),
                __('نسبة الحضور %'),
            ]);

            foreach ($stats as $stat) {
                fputcsv($handle, [
                    $stat->participant_number ?? '-',
                    $stat->name,
                    $stat->total_sessions,
                    $stat->present_count,
                    $stat->late_count,
                    $stat->partial_count,
                    $stat->absent_count,
                    $stat->excused_count,
                    $stat->rate . '%',
                ]);
            }

            fclose($handle);
        }, "{$filename}.csv", [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    public function render()
    {
        $branchId = $this->getActiveBranchId();

        $groups = TrainingGroup::query()
            ->orderBy('name_ar')
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->when($this->programFilter, fn ($q) => $q->where('training_program_id', $this->programFilter))
            ->get();

        // TrainingProgram is a SHARED model: branch_id NULL means "every branch
        // runs this programme". Hard-filtering on branch_id here dropped every
        // academy-wide programme out of the picker, so the branch's own groups
        // could not be filtered by the programme they actually belong to. The
        // global scope already returns this branch's rows plus the shared ones.
        $programs = TrainingProgram::query()
            ->orderBy('name_ar')
            ->get();

        $participantStats = $this->getParticipantStats();
        $summary = $this->getGroupSummary();

        return view('livewire.reports.attendance-report', [
            'groups' => $groups,
            'programs' => $programs,
            'participantStats' => $participantStats,
            'summary' => $summary,
        ]);
    }

    private function getFilteredSessionIds(mixed $branchId): Collection
    {
        return TrainingSession::query()
            ->when($this->groupFilter, fn ($q) => $q->where('training_group_id', $this->groupFilter))
            ->when(! $this->groupFilter && $this->programFilter, function ($q) {
                $q->whereHas('group', fn ($gq) => $gq->where('training_program_id', $this->programFilter));
            })
            ->when($branchId, fn ($q) => $q->whereHas('group', fn ($gq) => $gq->where('branch_id', $branchId)))
            ->when($this->dateFrom, fn ($q) => $q->where('session_date', '>=', $this->dateFrom))
            ->when($this->dateTo, fn ($q) => $q->where('session_date', '<=', $this->dateTo))
            ->whereIn('status', ['completed', 'in_progress', 'scheduled'])
            ->pluck('id');
    }
}
