<?php

namespace App\Http\Livewire;

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 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');
                        });
                    })
                    ->get();

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

                $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();

                $presenceIndex = [];

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

                foreach ($presenceRows 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' => [],
                ];

                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'  => [],
                    ];

                    $months = array_column(
                        array_filter(json_decode($course_member->months, true), fn($m) => $m['status'] != 2),
                        'm'
                    );
                    sort($months);

                    $when = json_decode($course_member->when, true);

                    $memberCalendars = [];

                    foreach ($when as $period) {
                        $days = $period['day'];

                        if (!$days) {
                            continue;
                        }

                        $fromTime = $period['from'];
                        $toTime   = $period['to'];

                        $ranges = $this->generateFiscalDateRanges($months, $days, $dayMap, $fromTime, $toTime);

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

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

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

                    $mid = $course_member->member_id;
                    $lastAttendance = $lastMakeupByMember[$mid] ?? null;
                    foreach ($memberCalendars as $calendar) {
                        $lessonFrom = Carbon::parse($calendar->from);

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

                        $pKey = $mid . '|' . $calendar->id;
                        $hasPresence = isset($presenceIndex[$pKey]);

                        if ($hasPresence) {
                            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');

        // $this->records = [];

        // $to = date("Y-m-d 23:59:59");
        // $calendars = \App\Models\Calendar::where('from', '<=', $to)->orderBy('from')->get();

        // $month = date("n");
        // $this->year = ($month >= 9) ? date("Y") : date("Y") - 1;

        // $start = date("Y-m-d H:i:s", mktime(0, 0, 0, 9, 1, $this->year));
        // $end   = date("Y-m-d 23:59:59");

        // $calendars = \App\Models\Calendar::whereBetween('from', [$start, $end])->orderBy('from')->get();

        // foreach ($calendars as $calendar) {

        //     $presences = \App\Models\Presence::where('calendar_id', $calendar->id)->where('status', '<>', 99);
        //     $presences = $presences->pluck('member_id')->toArray();
        //     $presences_annullate = \App\Models\Presence::where('calendar_id', $calendar->id)->where('status', 99)->pluck('member_id')->toArray();

        //     $days = ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'];
        //     $dow = date('w', strtotime($calendar->from));
        //     $d = $days[$dow];

        //     $h = date('H:i', strtotime($calendar->from));

        //     // Elenco corsi per tipologia in base al calendario
        //     $courses = \App\Models\Course::where('name', $calendar->name)->where('date_from', '<=', $calendar->from)->where('date_to', '>=', $calendar->to);
        //     $courses = $courses->pluck('id')->toArray();

        //     $months = date("n", strtotime($calendar->from));

        //     // Elenco utenti iscritti al corso "padre"
        //     $members = \App\Models\MemberCourse::where('when', 'like', "%" . $d . "%")
        //         ->where('when', 'like', '%"from":"' . $h . '"%')
        //         ->whereNot('months', 'like', '%"m":' . $months . ',"status":2%')
        //         ->whereDate('date_from', '<=', $calendar->from)
        //         ->whereDate('date_to', '>=', $calendar->from)
        //         ->whereIn('course_id', $courses)
        //         ->orderBy('member_id')
        //         ->get();
        //     //$members = \App\Models\MemberCourse::where('when', 'like', "%" . $d . "%")->where('when', 'like', '%"from":"' . $h . '"%')->whereIn('member_id', $presences)->whereIn('course_id', $courses)->get();
        //     foreach ($members as $member) {

        //         $presence = \App\Models\Presence::where('member_id', $member->member->id)->where('calendar_id', $calendar->id)->first();

        //         if (!in_array($member->member->id, $presences)) {
        //             if (!in_array($member->member->id, $presences_annullate)) {
        //                 if (array_key_exists($member->member->id, $this->records)) {
        //                     $this->records[$member->member->id]['total'] += 1;
        //                     $this->records[$member->member->id]['date'] .= " - " . date("d/m", strtotime($calendar->from));
        //                 } else
        //                     $this->records[$member->member->id] = array("last_name" => $member->member->last_name, "first_name" => $member->member->first_name, "course" => $calendar->name, "total" => 1, "date" => date("d/m", strtotime($calendar->from)));
        //             }
        //         } else {
        //             if (array_key_exists($member->member->id, $this->records))
        //                 unset($this->records[$member->member->id]);
        //         }
        //     }
        // }

        // array_multisort(array_column($this->records, 'total'), SORT_DESC, $this->records);

        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 generateFiscalDateRanges($months, $days, $dayMap, $fromTime, $toTime)
    {
        $limit = now()->yesterday()->endOfDay();

        $end_day = $limit;

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

        $allowedDow = collect($days)
            ->map(fn($d) => $dayMap[$d] ?? null)
            ->filter(fn($v) => $v !== null)
            ->unique()
            ->values();

        $fromCarbon = Carbon::parse($fromTime);
        $toCarbon = Carbon::parse($toTime);

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

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

        $ranges = [];

        foreach ($months as $month) {
            $yearForMonth = ($month >= $this->fiscalStartMonth)
                ? $startYear
                : $startYear + 1;

            if (
                $yearForMonth > $limit->year ||
                ($yearForMonth === $limit->year && $month > $limit->month)
            ) {
                continue;
            }

            $firstOfMonth = Carbon::create($yearForMonth, $month, 1)->startOfDay();

            foreach ($allowedDow as $dow) {
                $offset  = ($dow - $firstOfMonth->dayOfWeek + 7) % 7;
                $current = $firstOfMonth->copy()->addDays($offset);

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

                    if ($fromDateTime->gt($limit)) {
                        break;
                    }

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

                    $current->addWeek();
                }
            }
        }

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

        return $ranges;
    }
}
