<?php

namespace Modules\Attendance\Controllers;

use App\Controllers\BaseController;
use App\Models\AttendanceRecordModel;
use App\Models\AttendanceShiftModel;
use App\Models\DepartmentModel;
use App\Models\EmployeeModel;
use App\Traits\ExportsReport;
use Modules\Attendance\Services\AttendanceService;
use Modules\Attendance\Services\AutoPunchService;

class AttendanceController extends BaseController
{
    use ExportsReport;
    protected AttendanceRecordModel $records;
    protected AttendanceShiftModel $shifts;
    protected EmployeeModel $employees;
    protected DepartmentModel $departments;
    protected AttendanceService $service;
    protected AutoPunchService $autoPunch;

    public function initController(\CodeIgniter\HTTP\RequestInterface $request, \CodeIgniter\HTTP\ResponseInterface $response, \Psr\Log\LoggerInterface $logger)
    {
        parent::initController($request, $response, $logger);
        $this->records     = new AttendanceRecordModel();
        $this->shifts      = new AttendanceShiftModel();
        $this->employees   = new EmployeeModel();
        $this->departments = new DepartmentModel();
        $this->service     = new AttendanceService();
        $this->autoPunch   = new AutoPunchService();
    }

    public function index()
    {
        $date   = $this->request->getGet('date') ?: date('Y-m-d');
        $search = trim((string) $this->request->getGet('q'));

        $boardResult = $this->service->getTodayBoard($date, $search ?: null);
        $board       = $boardResult['board'];
        $stats       = $this->service->getTodayStats($date, $search ?: null);

        return view('Modules\Attendance\Views\dashboard\index', array_merge($this->baseViewData(), [
            'title'  => 'Attendance',
            'date'   => $date,
            'search' => $search,
            'board'  => $board,
            'stats'  => $stats,
            'pager'  => $boardResult['pager'],
        ]));
    }

    public function clockIn()
    {
        $employeeId = (int) $this->request->getPost('employee_id');
        $result     = $this->service->clockIn($employeeId, (int) $this->session->get('user_id'));

        if (! $result['success']) {
            return redirect()->back()->with('errors', $result['errors']);
        }

        return redirect()->back()->with('success', 'Employee clocked in successfully.');
    }

    public function clockOut()
    {
        $employeeId = (int) $this->request->getPost('employee_id');
        $result     = $this->service->clockOut($employeeId, (int) $this->session->get('user_id'));

        if (! $result['success']) {
            return redirect()->back()->with('errors', $result['errors']);
        }

        return redirect()->back()->with('success', 'Employee clocked out successfully.');
    }

    public function manualForm()
    {
        return view('Modules\Attendance\Views\manual\create', array_merge($this->baseViewData(), [
            'title'     => 'Manual Attendance',
            'employees' => $this->employees->searchActive(trim((string) $this->request->getGet('q')), 100),
            'shifts'    => $this->shifts->getActive(),
        ]));
    }

    public function manualStore()
    {
        if (! $this->validate([
            'employee_id'     => 'required|integer',
            'attendance_date' => 'required|valid_date[Y-m-d]',
        ])) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $result = $this->service->recordManual($this->request->getPost(), (int) $this->session->get('user_id'));
        if (! $result['success']) {
            return redirect()->back()->withInput()->with('errors', $result['errors']);
        }

        return redirect()->to('/attendance/manual')->with('success', 'Attendance recorded.');
    }

    public function monthly()
    {
        $month = (int) ($this->request->getGet('month') ?: date('n'));
        $year  = (int) ($this->request->getGet('year') ?: date('Y'));

        if ($month < 1) {
            $month = 12;
            $year--;
        } elseif ($month > 12) {
            $month = 1;
            $year++;
        }

        $employeeId   = $this->request->getGet('employee_id') ? (int) $this->request->getGet('employee_id') : null;
        $departmentId = $this->request->getGet('department_id') ? (int) $this->request->getGet('department_id') : null;

        $records = $this->records->getMonthlySummary($year, $month, $employeeId, $departmentId);

        $summaryByEmployee = [];
        foreach ($records as $r) {
            $eid = $r['employee_id'];
            if (! isset($summaryByEmployee[$eid])) {
                $summaryByEmployee[$eid] = [
                    'name'     => $r['full_name'],
                    'number'   => $r['employee_number'],
                    'present'  => 0,
                    'absent'   => 0,
                    'late'     => 0,
                    'leave'    => 0,
                    'overtime' => 0,
                    'days'     => 0,
                ];
            }
            $summaryByEmployee[$eid]['days']++;
            match ($r['status']) {
                'present', 'auto_present' => $summaryByEmployee[$eid]['present']++,
                'late'                    => $summaryByEmployee[$eid]['late']++,
                'absent'                  => $summaryByEmployee[$eid]['absent']++,
                'on_leave'                => $summaryByEmployee[$eid]['leave']++,
                default                   => null,
            };
            $summaryByEmployee[$eid]['overtime'] += (int) $r['overtime_minutes'];
        }

        $prevMonth = $month === 1 ? 12 : $month - 1;
        $prevYear  = $month === 1 ? $year - 1 : $year;
        $nextMonth = $month === 12 ? 1 : $month + 1;
        $nextYear  = $month === 12 ? $year + 1 : $year;

        $formatMins = static function (int $mins): string {
            if ($mins <= 0) {
                return '0';
            }
            $h = intdiv($mins, 60);
            $m = $mins % 60;

            return $h > 0 ? "{$h}h {$m}m" : "{$m}m";
        };

        if ($export = $this->exportSections(
            'attendance_monthly_' . $year . '_' . $month,
            'Monthly Attendance',
            [
                [
                    'title'   => 'Employee Summary',
                    'headers' => ['Employee', 'Number', 'Days', 'Present', 'Late', 'Absent', 'On Leave', 'Overtime'],
                    'rows'    => array_map(static fn ($sum) => [
                        $sum['name'] ?? '',
                        $sum['number'] ?? '',
                        (int) ($sum['days'] ?? 0),
                        (int) ($sum['present'] ?? 0),
                        (int) ($sum['late'] ?? 0),
                        (int) ($sum['absent'] ?? 0),
                        (int) ($sum['leave'] ?? 0),
                        $formatMins((int) ($sum['overtime'] ?? 0)),
                    ], array_values($summaryByEmployee)),
                ],
                [
                    'title'   => 'Daily Records',
                    'headers' => ['Date', 'Employee', 'Shift', 'In', 'Out', 'Status', 'Work Minutes', 'Source'],
                    'rows'    => array_map(static fn ($r) => [
                        $r['attendance_date'] ?? '',
                        $r['full_name'] ?? '',
                        $r['shift_name'] ?? '',
                        $r['clock_in'] ?? '',
                        $r['clock_out'] ?? '',
                        $r['status'] ?? '',
                        (int) ($r['work_minutes'] ?? 0),
                        $r['source'] ?? '',
                    ], $records),
                ],
            ],
            sprintf('%04d-%02d', $year, $month)
        )) {
            return $export;
        }

        return view('Modules\Attendance\Views\monthly\index', array_merge($this->baseViewData(), [
            'title'             => 'Monthly Attendance',
            'month'             => $month,
            'year'              => $year,
            'records'           => $records,
            'summaryByEmployee' => $summaryByEmployee,
            'employees'         => $this->employees->scopeForList(null, 'active')->orderBy('full_name', 'ASC')->findAll(),
            'departments'       => $this->departments->orderBy('name', 'ASC')->findAll(),
            'filters'           => compact('employeeId', 'departmentId'),
            'prevUrl'           => base_url("attendance/monthly?month={$prevMonth}&year={$prevYear}"),
            'nextUrl'           => base_url("attendance/monthly?month={$nextMonth}&year={$nextYear}"),
        ]));
    }

    public function lateArrivals()
    {
        $dateFrom   = $this->request->getGet('from') ?: date('Y-m-01');
        $dateTo     = $this->request->getGet('to') ?: date('Y-m-d');
        $employeeId = $this->request->getGet('employee_id') ? (int) $this->request->getGet('employee_id') : null;
        $records    = $this->records->getLateArrivals($dateFrom, $dateTo, $employeeId);

        if ($export = $this->exportTable(
            'late_arrivals',
            'Late Arrivals',
            ['Date', 'Employee', 'Number', 'Shift', 'Clock In', 'Late Minutes', 'Status'],
            array_map(static fn ($r) => [
                $r['attendance_date'] ?? '',
                $r['full_name'] ?? '',
                $r['employee_number'] ?? '',
                $r['shift_name'] ?? '',
                $r['clock_in'] ?? '',
                (int) ($r['late_minutes'] ?? 0),
                $r['status'] ?? '',
            ], $records),
            $dateFrom . ' to ' . $dateTo
        )) {
            return $export;
        }

        return view('Modules\Attendance\Views\reports\late', array_merge($this->baseViewData(), [
            'title'     => 'Late Arrivals',
            'records'   => $records,
            'employees' => $this->employees->scopeForList(null, 'active')->orderBy('full_name', 'ASC')->findAll(),
            'filters'   => compact('dateFrom', 'dateTo', 'employeeId'),
        ]));
    }

    public function earlyExits()
    {
        $dateFrom   = $this->request->getGet('from') ?: date('Y-m-01');
        $dateTo     = $this->request->getGet('to') ?: date('Y-m-d');
        $employeeId = $this->request->getGet('employee_id') ? (int) $this->request->getGet('employee_id') : null;
        $records    = $this->records->getEarlyExits($dateFrom, $dateTo, $employeeId);

        if ($export = $this->exportTable(
            'early_exits',
            'Early Exits',
            ['Date', 'Employee', 'Number', 'Shift', 'Clock Out', 'Early Minutes', 'Status'],
            array_map(static fn ($r) => [
                $r['attendance_date'] ?? '',
                $r['full_name'] ?? '',
                $r['employee_number'] ?? '',
                $r['shift_name'] ?? '',
                $r['clock_out'] ?? '',
                (int) ($r['early_exit_minutes'] ?? $r['early_minutes'] ?? 0),
                $r['status'] ?? '',
            ], $records),
            $dateFrom . ' to ' . $dateTo
        )) {
            return $export;
        }

        return view('Modules\Attendance\Views\reports\early_exit', array_merge($this->baseViewData(), [
            'title'     => 'Early Exits',
            'records'   => $records,
            'employees' => $this->employees->scopeForList(null, 'active')->orderBy('full_name', 'ASC')->findAll(),
            'filters'   => compact('dateFrom', 'dateTo', 'employeeId'),
        ]));
    }

    public function overtime()
    {
        $dateFrom   = $this->request->getGet('from') ?: date('Y-m-01');
        $dateTo     = $this->request->getGet('to') ?: date('Y-m-d');
        $employeeId = $this->request->getGet('employee_id') ? (int) $this->request->getGet('employee_id') : null;
        $records    = $this->records->getOvertimeRecords($dateFrom, $dateTo, $employeeId);

        if ($export = $this->exportTable(
            'overtime',
            'Overtime',
            ['Date', 'Employee', 'Number', 'Shift', 'Clock Out', 'Overtime Minutes', 'Work Minutes'],
            array_map(static fn ($r) => [
                $r['attendance_date'] ?? '',
                $r['full_name'] ?? '',
                $r['employee_number'] ?? '',
                $r['shift_name'] ?? '',
                $r['clock_out'] ?? '',
                (int) ($r['overtime_minutes'] ?? 0),
                (int) ($r['work_minutes'] ?? 0),
            ], $records),
            $dateFrom . ' to ' . $dateTo
        )) {
            return $export;
        }

        return view('Modules\Attendance\Views\reports\overtime', array_merge($this->baseViewData(), [
            'title'     => 'Overtime',
            'records'   => $records,
            'employees' => $this->employees->scopeForList(null, 'active')->orderBy('full_name', 'ASC')->findAll(),
            'filters'   => compact('dateFrom', 'dateTo', 'employeeId'),
        ]));
    }

    public function autoPunch()
    {
        $date   = $this->request->getPost('date') ?: date('Y-m-d', strtotime('-1 day'));
        $result = $this->autoPunch->runForDate($date, (int) $this->session->get('user_id'));

        $msg = sprintf(
            'Auto punch complete for %s: %d present, %d absent, %d on leave, %d holiday, %d skipped.',
            $result['date'],
            $result['auto_present'],
            $result['absent'],
            $result['on_leave'],
            $result['holiday'],
            $result['skipped']
        );

        return redirect()->back()->with('success', $msg);
    }
}
