<?php

namespace App\Http\Livewire;

use App\Http\Middleware\TenantMiddleware;
use Illuminate\Support\Carbon;
use Livewire\Component;

class AbsenceReport extends Component
{
    public $records;
    public $record_assenze;
    public $original_record_assenze;

    public $fiscalStartMonth = 9;
    public $year;

    public $search;

    public function boot()
    {
        app(TenantMiddleware::class)->setupTenantConnection();
    }

    public function mount()
    {
        setlocale(LC_ALL, 'it_IT');

        $this->record_assenze = [];

        $end_day = now()->yesterday();
        $limit = $end_day->endOfDay();

        $this->year = ($end_day->month >= $this->fiscalStartMonth) ? $end_day->year : $end_day->year - 1;

        $dayMap = [
            'lun' => 1,
            'mar' => 2,
            'mer' => 3,
            'gio' => 4,
            'ven' => 5,
            'sab' => 6,
            'dom' => 0,
        ];

        try {
            $courses = \App\Models\Course::whereDate('date_from', '<=', $limit)
                ->whereDate('date_to', '>=', $limit)
                ->where('active', true)
                ->where('enabled', true)
                ->get();

            foreach ($courses as $course) {
                $course_members = \App\Models\MemberCourse::with(['member' => function ($q) {
                    $q->where(function ($q) {
                        $q->where('is_archived', false)->orWhereNull('is_archived');
                    })->where(function ($q) {
                        $q->where('is_deleted', false)->orWhereNull('is_deleted');
                    });
                }])
                    ->where('course_id', $course->id)
                    ->whereHas('member', function ($q) {
                        $q->where(function ($q) {
                            $q->where('is_archived', false)->orWhereNull('is_archived');
                        })->where(function ($q) {
                            $q->where('is_deleted', false)->orWhereNull('is_deleted');
                        });
                    })
                    ->whereDate('date_from', '<=', $limit)
                    ->whereDate('date_to', '>=', Carbon::parse($course->date_from)->startOfDay())
                    ->get();

                if ($course_members->isEmpty()) {
                    continue;
                }

                $courseCalendars = \App\Models\Calendar::where('course_id', $course->id)->where('from', '<=', $limit)->where('status', '<>', 99)->get();

                if ($courseCalendars->isEmpty()) {
                    continue;
                }

                $calendarIndex = [];
                foreach ($courseCalendars as $cal) {
                    $fromKey = Carbon::parse($cal->from)->toDateTimeString();
                    $toKey = Carbon::parse($cal->to)->toDateTimeString();
                    $key = $fromKey . '|' . $toKey;

                    $calendarIndex[$key] = $cal;
                }

                $memberIds = $course_members->pluck('member_id')->unique()->values()->all();

                $presences = \App\Models\Presence::query()
                    ->whereIn('calendar_id', $courseCalendars->pluck('id')->all())
                    ->whereIn('member_id', $memberIds)
                    ->where('status', '<>', 99)
                    ->get(['member_id', 'calendar_id']);

                $presenceIndex = [];
                foreach ($presences as $p) {
                    $presenceIndex[$p->member_id . '|' . $p->calendar_id] = true;
                }

                $makeups = \App\Models\Presence::query()
                    ->join('calendars', 'presences.calendar_id', '=', 'calendars.id')
                    ->where('presences.motivation_course_id', $course->id)
                    ->whereIn('presences.member_id', $memberIds)
                    ->where('presences.status', '<>', 99)
                    ->where('calendars.from', '<=', $limit)
                    ->where('calendars.status', '<>', 99)
                    ->selectRaw('presences.member_id, MAX(calendars.from) as last_makeup_from')
                    ->groupBy('presences.member_id')
                    ->get();

                $lastMakeupByMember = [];
                foreach ($makeups as $m) {
                    $lastMakeupByMember[$m->member_id] = Carbon::parse($m->last_makeup_from);
                }

                $this->record_assenze[$course->id] = [
                    'course' => [
                        'id' => $course->id,
                        'name' => $course->getDetailsName(),
                    ],
                    'members' => [],
                ];

                $courseWhen = json_decode($course->when ?? '[]', true);
                if (!is_array($courseWhen) || empty($courseWhen)) {
                    unset($this->record_assenze[$course->id]);
                    continue;
                }

                foreach ($course_members as $course_member) {
                    $this->record_assenze[$course->id]['members'][$course_member->id] = [
                        'member' => [
                            'id' => $course_member->member->id,
                            'first_name' => $course_member->member->first_name,
                            'last_name' => $course_member->member->last_name,
                        ],
                        'count' => 0,
                        'dates' => [],
                    ];

                    $mid = $course_member->member_id;

                    $rangeStart = Carbon::parse(max($course->date_from, $course_member->date_from))->startOfDay();
                    $rangeEnd = Carbon::parse(min($course->date_to, $course_member->date_to, $limit->toDateString()))->endOfDay();

                    if ($rangeStart->gt($rangeEnd)) {
                        unset($this->record_assenze[$course->id]['members'][$course_member->id]);
                        continue;
                    }

                    $memberCalendars = [];

                    foreach ($courseWhen as $period) {
                        $days = $period['day'] ?? [];
                        if (empty($days)) continue;

                        $fromTime = $period['from'] ?? null;
                        $toTime   = $period['to'] ?? null;
                        if (!$fromTime || !$toTime) continue;

                        $ranges = $this->generateDateRanges(
                            $rangeStart,
                            $rangeEnd,
                            $days,
                            $dayMap,
                            $fromTime,
                            $toTime
                        );

                        foreach ($ranges as $range) {
                            $key = $range['from']->toDateTimeString() . '|' . $range['to']->toDateTimeString();

                            if (isset($calendarIndex[$key])) {
                                $memberCalendars[] = $calendarIndex[$key];
                            }
                        }
                    }

                    if (empty($memberCalendars)) {
                        unset($this->record_assenze[$course->id]['members'][$course_member->id]);
                        continue;
                    }

                    usort($memberCalendars, fn($a, $b) => $b->to <=> $a->to);

                    $lastAttendance = $lastMakeupByMember[$mid] ?? null;

                    foreach ($memberCalendars as $calendar) {
                        $lessonFrom = Carbon::parse($calendar->from);

                        if ($lastAttendance && $lessonFrom->lte($lastAttendance)) {
                            break;
                        }

                        $pKey = $mid . '|' . $calendar->id;
                        if (isset($presenceIndex[$pKey])) {
                            break;
                        }

                        // assenza vera
                        $this->record_assenze[$course->id]['members'][$course_member->id]['count']++;
                        $this->record_assenze[$course->id]['members'][$course_member->id]['dates'][] = [
                            'calendar_id' => $calendar->id,
                            'date' => $lessonFrom->translatedFormat('d/m'),
                        ];
                    }

                    if ($this->record_assenze[$course->id]['members'][$course_member->id]['count'] < 2) {
                        unset($this->record_assenze[$course->id]['members'][$course_member->id]);
                    }
                }

                if (empty($this->record_assenze[$course->id]['members'])) {
                    unset($this->record_assenze[$course->id]);
                } else {
                    $members = $this->record_assenze[$course->id]['members'];
                    usort($members, function ($a, $b) {
                        if ($a['count'] !== $b['count']) {
                            return $b['count'] <=> $a['count'];
                        }

                        $last = strcmp($a['member']['last_name'], $b['member']['last_name']);
                        if ($last !== 0) return $last;

                        return strcmp($a['member']['first_name'], $b['member']['first_name']);
                    });
                    $this->record_assenze[$course->id]['members'] = $members;
                }
            }

            usort($this->record_assenze, fn($a, $b) => $a['course']['name'] <=> $b['course']['name']);
        } catch (\Throwable $e) {
            dd($e->getMessage());
        }

        $this->original_record_assenze = $this->record_assenze;
    }

    public function render()
    {
        setlocale(LC_ALL, 'it_IT');

        return view('livewire.absence_report');
    }

    public function applySearch()
    {
        $this->search = trim($this->search);
        $this->filter();
    }

    public function resetSearch()
    {
        $this->search = '';
        $this->record_assenze = $this->original_record_assenze;
    }

    protected function filter()
    {
        $this->record_assenze = $this->original_record_assenze;

        if ($this->search === '') return;

        $needle = mb_strtolower($this->search);

        foreach ($this->record_assenze as $courseId => $courseData) {
            $courseData['members'] = array_values(array_filter($courseData['members'], function ($m) use ($needle) {
                $full  = mb_strtolower((trim($m['member']['last_name']) ?? '') . ' ' . (trim($m['member']['first_name']) ?? ''));
                $full2 = mb_strtolower((trim($m['member']['first_name']) ?? '') . ' ' . (trim($m['member']['last_name']) ?? ''));
                return str_contains($full, $needle) || str_contains($full2, $needle);
            }));

            if (empty($courseData['members'])) {
                unset($this->record_assenze[$courseId]);
            } else {
                $this->record_assenze[$courseId] = $courseData;
            }
        }
    }

    protected function generateDateRanges($rangeStart, $rangeEnd, $days, $dayMap, $fromTime, $toTime)
    {
        $allowedDow = collect($days)
            ->map(fn($d) => $dayMap[$d] ?? null)
            ->filter(fn($v) => $v !== null)
            ->unique()
            ->values()
            ->all();

        // parse orari
        $fromC = Carbon::parse($fromTime);
        $toC = Carbon::parse($toTime);

        $fromHour = $fromC->hour;
        $fromMinute = $fromC->minute;
        $fromSecond = $fromC->second;

        $toHour = $toC->hour;
        $toMinute = $toC->minute;
        $toSecond = $toC->second;

        $ranges = [];

        foreach ($allowedDow as $dow) {
            $current = $rangeStart->copy()->startOfDay();

            $offset  = ($dow - $current->dayOfWeek + 7) % 7;
            $current->addDays($offset);

            while ($current->lte($rangeEnd)) {
                $fromDateTime = $current->copy()->setTime($fromHour, $fromMinute, $fromSecond);
                $toDateTime = $current->copy()->setTime($toHour, $toMinute, $toSecond);

                if ($fromDateTime->lt($rangeStart)) {
                    $current->addWeek();
                    continue;
                }
                if ($fromDateTime->gt($rangeEnd)) {
                    break;
                }

                $ranges[] = [
                    'from' => $fromDateTime,
                    'to' => $toDateTime,
                ];

                $current->addWeek();
            }
        }

        usort($ranges, fn($a, $b) => $a['from'] <=> $b['from']);
        return $ranges;
    }
}
