<?php
// ============================================================
//  MJPOS – Employee Attendance Controller
// ============================================================
class AttendanceController extends Controller
{
    // ── Monthly Attendance Sheet ──────────────────────────────
    public function sheet(): void
    {
        Auth::requireLogin();
        $month = (int)$this->get('month', date('n'));
        $year  = (int)$this->get('year',  date('Y'));
        $empId = (int)$this->get('emp', 0);

        // Days in month
        $daysInMonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);
        $monthStart  = sprintf('%04d-%02d-01', $year, $month);
        $monthEnd    = sprintf('%04d-%02d-%02d', $year, $month, $daysInMonth);

        $employees = Database::fetchAll(
            "SELECT e.id, e.emp_code, e.full_name, e.designation, e.department, e.fingerprint_id
             FROM employees e WHERE e.status='active' ORDER BY e.full_name"
        );

        // Build attendance map [emp_id][day] => record
        $attWhere  = $empId ? "AND ea.employee_id=$empId" : '';
        $attRecs   = Database::fetchAll(
            "SELECT ea.*, e.full_name, e.emp_code
             FROM employee_attendance ea
             JOIN employees e ON e.id=ea.employee_id
             WHERE ea.att_date BETWEEN ? AND ? $attWhere
             ORDER BY ea.employee_id, ea.att_date",
            [$monthStart, $monthEnd]
        );

        $attMap = [];
        foreach ($attRecs as $r) {
            $day = (int)date('j', strtotime($r['att_date']));
            $attMap[$r['employee_id']][$day] = $r;
        }

        // Recent fingerprint logs (unprocessed)
        $fpLogs = Database::fetchAll(
            "SELECT fl.*, e.full_name, e.emp_code
             FROM fingerprint_logs fl
             LEFT JOIN employees e ON e.id=fl.employee_id
             WHERE fl.punch_time >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
             ORDER BY fl.punch_time DESC LIMIT 30"
        );

        $pageTitle  = 'Attendance Sheet — '.date('F Y', mktime(0,0,0,$month,1,$year));
        $breadcrumb = [['label'=>'Payroll','url'=>APP_URL.'/payroll'],['label'=>'Attendance']];
        ob_start(); require APP_PATH.'/views/payroll/attendance.php';
        $content = ob_get_clean();
        require APP_PATH.'/views/layouts/main.php';
    }

    // ── Save single attendance record ────────────────────────
    public function save(): void
    {
        Auth::requireLogin();
        Auth::requireCsrf();

        $empId  = (int)$this->post('employee_id');
        $date   = $this->post('att_date');
        $status = Security::enum($this->post('status','present'),
            ['present','absent','half_day','late','leave','holiday','off'], 'present');
        $checkIn  = $this->post('check_in','') ?: null;
        $checkOut = $this->post('check_out','') ?: null;
        $method   = Security::enum($this->post('method','manual'),
            ['manual','fingerprint','qr','face','card'], 'manual');
        $note   = Security::strip($this->post('note',''));

        $workedHours = 0;
        $otHours     = 0;
        if ($checkIn && $checkOut) {
            $inTs  = strtotime($checkIn);
            $outTs = strtotime($checkOut);
            $diff  = max(0, ($outTs - $inTs) / 3600);
            $workedHours = round(min($diff, 12), 2);
            $otHours     = round(max(0, $diff - 9), 2);
        }

        Database::execute(
            "INSERT INTO employee_attendance
               (employee_id,att_date,check_in,check_out,worked_hours,ot_hours,status,method,note,recorded_by)
             VALUES (?,?,?,?,?,?,?,?,?,?)
             ON DUPLICATE KEY UPDATE
               check_in=VALUES(check_in),check_out=VALUES(check_out),
               worked_hours=VALUES(worked_hours),ot_hours=VALUES(ot_hours),
               status=VALUES(status),method=VALUES(method),note=VALUES(note),
               recorded_by=VALUES(recorded_by)",
            [$empId,$date,$checkIn,$checkOut,$workedHours,$otHours,$status,$method,$note,Auth::id()]
        );

        $month = (int)date('n', strtotime($date));
        $year  = (int)date('Y', strtotime($date));
        redirectWith(APP_URL."/payroll/attendance?month={$month}&year={$year}",
            'success', 'Attendance saved.');
    }

    // ── Bulk save (whole month sheet) ────────────────────────
    public function bulkSave(): void
    {
        Auth::requireLogin();
        Auth::requireCsrf();

        $month  = (int)$this->post('month', date('n'));
        $year   = (int)$this->post('year',  date('Y'));
        $empIds = (array)$this->post('employee_id', []);
        $days   = (array)$this->post('days', []);

        foreach ($empIds as $idx => $empId) {
            if (!isset($days[$idx])) continue;
            foreach ($days[$idx] as $day => $status) {
                $date     = sprintf('%04d-%02d-%02d', $year, $month, $day);
                $checkIn  = $this->post("check_in_{$empId}_{$day}") ?: null;
                $checkOut = $this->post("check_out_{$empId}_{$day}") ?: null;
                $wh = 0; $oth = 0;
                if ($checkIn && $checkOut) {
                    $diff = max(0,(strtotime($checkOut)-strtotime($checkIn))/3600);
                    $wh   = round(min($diff,12),2);
                    $oth  = round(max(0,$diff-9),2);
                }
                Database::execute(
                    "INSERT INTO employee_attendance
                       (employee_id,att_date,check_in,check_out,worked_hours,ot_hours,status,method,recorded_by)
                     VALUES (?,?,?,?,?,?,?,'manual',?)
                     ON DUPLICATE KEY UPDATE status=VALUES(status),check_in=VALUES(check_in),
                       check_out=VALUES(check_out),worked_hours=VALUES(worked_hours),
                       ot_hours=VALUES(ot_hours),recorded_by=VALUES(recorded_by)",
                    [$empId,$date,$checkIn,$checkOut,$wh,$oth,$status,Auth::id()]
                );
            }
        }
        redirectWith(APP_URL."/payroll/attendance?month={$month}&year={$year}",
            'success', "Attendance sheet saved for ".date('F Y',mktime(0,0,0,$month,1,$year)).'.');
    }

    // ── Process fingerprint log (import from device) ─────────
    public function processFingerprint(): void
    {
        Auth::requireLogin();
        Auth::requireCsrf();

        // Get pending fingerprint logs
        $logs = Database::fetchAll(
            "SELECT * FROM fingerprint_logs WHERE status='pending' ORDER BY punch_time"
        );

        $processed = 0; $errors = 0;

        foreach ($logs as $log) {
            // Find employee by fingerprint_id
            $emp = Database::fetchOne(
                'SELECT id FROM employees WHERE fingerprint_id=? AND status=\'active\'',
                [$log['device_user_id']]
            );

            if (!$emp) {
                Database::execute(
                    "UPDATE fingerprint_logs SET status='error',error_msg='Employee not found for FP ID: {$log['device_user_id']}' WHERE id=?",
                    [$log['id']]
                );
                $errors++;
                continue;
            }

            $date     = date('Y-m-d', strtotime($log['punch_time']));
            $time     = date('H:i:s', strtotime($log['punch_time']));
            $existing = Database::fetchOne(
                'SELECT * FROM employee_attendance WHERE employee_id=? AND att_date=?',
                [$emp['id'], $date]
            );

            if ($log['punch_type'] === 'in') {
                if (!$existing) {
                    Database::execute(
                        "INSERT INTO employee_attendance (employee_id,att_date,check_in,status,method,fingerprint_id,device_id,recorded_by)
                         VALUES (?,?,?,'present','fingerprint',?,?,?)",
                        [$emp['id'],$date,$time,$log['device_user_id'],$log['device_id'],Auth::id()]
                    );
                } else {
                    Database::execute(
                        "UPDATE employee_attendance SET check_in=?,fingerprint_id=?,device_id=?,method='fingerprint' WHERE id=?",
                        [$time,$log['device_user_id'],$log['device_id'],$existing['id']]
                    );
                }
            } elseif ($log['punch_type'] === 'out') {
                if ($existing) {
                    $inTs  = $existing['check_in'] ? strtotime($date.' '.$existing['check_in']) : 0;
                    $outTs = strtotime($date.' '.$time);
                    $diff  = $inTs > 0 ? max(0,($outTs-$inTs)/3600) : 0;
                    $wh    = round(min($diff,12),2);
                    $oth   = round(max(0,$diff-9),2);
                    Database::execute(
                        "UPDATE employee_attendance SET check_out=?,worked_hours=?,ot_hours=? WHERE id=?",
                        [$time,$wh,$oth,$existing['id']]
                    );
                }
            }

            Database::execute(
                "UPDATE fingerprint_logs SET status='processed',employee_id=? WHERE id=?",
                [$emp['id'],$log['id']]
            );
            $processed++;
        }

        $month = (int)date('n');
        $year  = (int)date('Y');
        redirectWith(APP_URL."/payroll/attendance?month={$month}&year={$year}",
            'success', "Fingerprint sync: {$processed} processed, {$errors} errors.");
    }

    // ── Simulate fingerprint punch (manual entry via UI) ─────
    public function fingerprintPunch(): void
    {
        Auth::requireLogin();
        Auth::requireCsrf();

        $fpId  = Security::strip($this->post('fingerprint_id',''));
        $type  = Security::enum($this->post('punch_type','in'),['in','out'],'in');
        $time  = $this->post('punch_time', date('Y-m-d H:i:s'));

        $emp = Database::fetchOne(
            "SELECT id,full_name FROM employees WHERE fingerprint_id=? AND status='active'",[$fpId]
        );

        if (!$emp) {
            echo json_encode(['success'=>false,'message'=>"No employee found for Fingerprint ID: {$fpId}"]);
            return;
        }

        Database::execute(
            "INSERT INTO fingerprint_logs (device_id,device_user_id,employee_id,punch_time,punch_type,status)
             VALUES ('MANUAL',?,?,?,'$type','pending')",
            [$fpId,$emp['id'],$time]
        );

        echo json_encode([
            'success' => true,
            'message' => "Punch recorded for {$emp['full_name']} ({$type})",
            'employee'=> $emp['full_name'],
            'type'    => $type,
            'time'    => $time,
        ]);
    }

    // ── Summary stats for payroll calculation ────────────────
    public function getSummary(int $empId, int $month, int $year): array
    {
        $monthStart = sprintf('%04d-%02d-01', $year, $month);
        $monthEnd   = sprintf('%04d-%02d-%02d', $year, $month, cal_days_in_month(CAL_GREGORIAN,$month,$year));

        return Database::fetchOne(
            "SELECT
               COUNT(CASE WHEN status='present' THEN 1 END)  AS present_days,
               COUNT(CASE WHEN status='late' THEN 1 END)     AS late_days,
               COUNT(CASE WHEN status='absent' THEN 1 END)   AS absent_days,
               COUNT(CASE WHEN status='half_day' THEN 1 END) AS half_days,
               COUNT(CASE WHEN status='leave' THEN 1 END)    AS leave_days,
               COALESCE(SUM(worked_hours),0)                  AS total_worked_hrs,
               COALESCE(SUM(ot_hours),0)                      AS total_ot_hrs
             FROM employee_attendance
             WHERE employee_id=? AND att_date BETWEEN ? AND ?",
            [$empId,$monthStart,$monthEnd]
        ) ?? [];
    }

    // ── FP Upload Page ────────────────────────────────────────
    public function fpUploadPage(): void
    {
        Auth::requireLogin();
        $pageTitle  = 'Fingerprint Data Uploader';
        $breadcrumb = [
            ['label'=>'Payroll','url'=>APP_URL.'/payroll'],
            ['label'=>'Attendance','url'=>APP_URL.'/payroll/attendance'],
            ['label'=>'FP Upload'],
        ];
        ob_start(); require APP_PATH.'/views/payroll/fp_upload.php';
        $content = ob_get_clean();
        require APP_PATH.'/views/layouts/main.php';
    }

    // ── Download CSV Template ─────────────────────────────────
    public function downloadTemplate(): void
    {
        Auth::requireLogin();
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="fingerprint_template.csv"');
        $out = fopen('php://output','w');
        fputcsv($out,['UserID','Date','Time','Status','DeviceID']);
        // Get active employees
        $emps = Database::fetchAll(
            "SELECT fingerprint_id,emp_code,full_name FROM employees WHERE status='active' ORDER BY emp_code"
        );
        foreach ($emps as $e) {
            fputcsv($out,[
                $e['fingerprint_id'] ?: $e['emp_code'],
                date('Y-m-d'),
                '08:00:00',
                '0',
                'ZK-DEVICE-01'
            ]);
            fputcsv($out,[
                $e['fingerprint_id'] ?: $e['emp_code'],
                date('Y-m-d'),
                '17:00:00',
                '1',
                'ZK-DEVICE-01'
            ]);
        }
        fclose($out);
        exit;
    }

    // ── Import parsed rows from JS ────────────────────────────
    public function fpImport(): void
    {
        Auth::requireLogin();
        Auth::requireCsrf();
        header('Content-Type: application/json');

        $rows      = json_decode($this->post('rows','[]'), true);
        $imported  = 0;
        $errors    = 0;

        foreach ((array)$rows as $row) {
            $fpId = trim($row['empId'] ?? '');
            $date = $this->parseDate(trim($row['date'] ?? ''));
            $time = trim($row['time'] ?? '') ?: null;
            $type = $row['type'] ?? 'in';

            if (!$fpId || !$date) { $errors++; continue; }

            // Find employee by fingerprint_id OR emp_code
            $emp = Database::fetchOne(
                "SELECT id FROM employees WHERE (fingerprint_id=? OR emp_code=?) AND status='active'",
                [$fpId, $fpId]
            );
            if (!$emp) { $errors++; continue; }

            $existing = Database::fetchOne(
                'SELECT * FROM employee_attendance WHERE employee_id=? AND att_date=?',
                [$emp['id'], $date]
            );

            if ($type === 'in') {
                if (!$existing) {
                    Database::execute(
                        "INSERT INTO employee_attendance
                           (employee_id,att_date,check_in,status,method,fingerprint_id,recorded_by)
                         VALUES (?,?,?,'present','fingerprint',?,?)",
                        [$emp['id'],$date,$time,$fpId,Auth::id()]
                    );
                } elseif (!$existing['check_in']) {
                    Database::execute(
                        'UPDATE employee_attendance SET check_in=?,method=\'fingerprint\',fingerprint_id=? WHERE id=?',
                        [$time,$fpId,$existing['id']]
                    );
                }
            } elseif ($type === 'out' && $existing) {
                $inTs  = $existing['check_in'] ? strtotime($date.' '.$existing['check_in']) : 0;
                $outTs = $time ? strtotime($date.' '.$time) : 0;
                $wh    = ($inTs && $outTs) ? round(min(max(0,($outTs-$inTs)/3600),12),2) : 0;
                $oth   = max(0, $wh - 9);
                Database::execute(
                    'UPDATE employee_attendance SET check_out=?,worked_hours=?,ot_hours=? WHERE id=?',
                    [$time, $wh, $oth, $existing['id']]
                );
            }

            // Log to fingerprint_logs
            if ($time) {
                Database::execute(
                    "INSERT INTO fingerprint_logs (device_id,device_user_id,employee_id,punch_time,punch_type,status)
                     VALUES ('IMPORT',?,?,?,'$type','processed')",
                    [$fpId,$emp['id'],$date.' '.$time]
                );
            }
            $imported++;
        }

        Auth::logAudit(Auth::id(),'fp_import','attendance',"FP import: {$imported} records, {$errors} errors");
        echo json_encode(['imported'=>$imported,'errors'=>$errors]);
        exit;
    }

    private function parseDate(string $raw): string
    {
        $raw = trim($raw);
        if (!$raw) return '';
        // Try common formats
        $formats = ['Y-m-d','d/m/Y','m/d/Y','d-m-Y','Ymd','d M Y','Y/m/d'];
        foreach ($formats as $fmt) {
            $dt = \DateTime::createFromFormat($fmt, $raw);
            if ($dt) return $dt->format('Y-m-d');
        }
        // Fallback strtotime
        $ts = strtotime($raw);
        return $ts ? date('Y-m-d',$ts) : '';
    }
}