<?php
/**
 * Personalliggare — business logic (clock in/out, sessions, PIN).
 *
 * @package Maca_Menu_List
 */

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Whether personalliggare is enabled.
 *
 * @return bool
 */
function maca_menulist_attendance_enabled() {
    return get_option('maca_menulist_attendance_enabled', '1') === '1';
}

/**
 * Swedish UI copy for personalliggare pages, API responses, and saved reports.
 *
 * @param string $msgid English message id.
 * @return string
 */
function maca_menulist_attendance_ui_string($msgid) {
    static $sv_catalog = null;

    $msgid = (string) $msgid;

    if ($sv_catalog === null && function_exists('maca_menulist_get_guest_translation_catalog')) {
        $sv_catalog = maca_menulist_get_guest_translation_catalog('sv');
        if (!is_array($sv_catalog)) {
            $sv_catalog = array();
        }
    }

    if (is_array($sv_catalog) && isset($sv_catalog[ $msgid ])) {
        $value = $sv_catalog[ $msgid ];
        if (is_string($value) && $value !== '' && $value !== $msgid) {
            return $value;
        }
    }

    if (function_exists('maca_menulist_resolve_guest_string')) {
        $resolved = maca_menulist_resolve_guest_string($msgid, true);
        if (is_string($resolved) && $resolved !== '' && $resolved !== $msgid) {
            return $resolved;
        }
    }

    return $msgid;
}

/**
 * HTML lang attribute for personalliggare pages.
 *
 * @return string
 */
function maca_menulist_attendance_html_lang() {
    return 'sv';
}

/**
 * Format a calendar date label in Swedish for personalliggare views.
 *
 * @param string $date Date Y-m-d.
 * @return string
 */
function maca_menulist_attendance_format_date_label($date) {
    $date = maca_menulist_attendance_normalize_date($date);
    $timestamp = strtotime($date . ' 12:00:00');

    if ($timestamp === false) {
        return $date;
    }

    $switched = false;
    if (function_exists('switch_to_locale')) {
        $switched = switch_to_locale('sv_SE');
    }

    $label = wp_date(get_option('date_format'), $timestamp);

    if ($switched && function_exists('restore_previous_locale')) {
        restore_previous_locale();
    }

    return $label;
}

/**
 * Format a datetime label in Swedish for personalliggare views.
 *
 * @param string $datetime MySQL datetime.
 * @return string
 */
function maca_menulist_attendance_format_datetime($datetime) {
    $switched = false;
    if (function_exists('switch_to_locale')) {
        $switched = switch_to_locale('sv_SE');
    }

    $label = maca_menulist_format_wp_datetime($datetime);

    if ($switched && function_exists('restore_previous_locale')) {
        restore_previous_locale();
    }

    return $label;
}

/**
 * Public slug for the clock-in page.
 *
 * @return string
 */
function maca_menulist_get_attendance_slug() {
    $slug = get_option('maca_menulist_attendance_slug', 'stampla');
    $slug = sanitize_title($slug);

    if ($slug === '') {
        $slug = 'stampla';
    }

    return $slug;
}

/**
 * Public URL for mobile clock-in.
 *
 * @return string
 */
function maca_menulist_get_attendance_url() {
    return home_url('/' . maca_menulist_get_attendance_slug() . '/');
}

/**
 * Public URL for the in-venue control view (Skatteverket).
 *
 * @return string
 */
function maca_menulist_get_attendance_control_url() {
    return home_url('/' . maca_menulist_get_attendance_slug() . '/kontroll/');
}

/**
 * Attendance page view from the request.
 *
 * @return string clock|kontroll
 */
function maca_menulist_get_attendance_view() {
    $view = sanitize_key((string) get_query_var('maca_attendance_view'));

    return $view === 'kontroll' ? 'kontroll' : 'clock';
}

/**
 * Format personal number for reports (YYYYMMDD-XXXX).
 *
 * @param string $value Normalized digits.
 * @return string
 */
function maca_menulist_format_personal_number_display($value) {
    $value = maca_menulist_normalize_personal_number($value);
    if (strlen($value) === 12) {
        $value = substr($value, 2);
    }

    if (strlen($value) === 10) {
        return substr($value, 0, 8) . '-' . substr($value, 8);
    }

    return $value;
}

/**
 * Normalize report date input.
 *
 * @param string $date Raw date.
 * @return string Y-m-d
 */
function maca_menulist_attendance_normalize_date($date) {
    $date = sanitize_text_field((string) $date);
    if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
        return $date;
    }

    return function_exists('maca_menulist_wp_today') ? maca_menulist_wp_today() : wp_date('Y-m-d');
}

/**
 * Transient key for attendance control session.
 *
 * @param string $token Session token.
 * @return string
 */
function maca_menulist_attendance_control_session_storage_key($token) {
    $token = strtolower(preg_replace('/[^a-f0-9]/', '', (string) $token));

    if (strlen($token) !== 64) {
        return '';
    }

    return 'maca_att_ctrl_' . $token;
}

/**
 * Create session after staff PIN verification on control view.
 *
 * @return string Session token.
 */
function maca_menulist_create_attendance_control_session() {
    try {
        $token = bin2hex(random_bytes(32));
    } catch (Throwable $e) {
        $token = wp_hash(wp_generate_password(32, true, true) . microtime(true));
        $token = strtolower(preg_replace('/[^a-f0-9]/', '', $token));
        if (strlen($token) < 64) {
            $token = str_pad($token, 64, '0');
        }
        $token = substr($token, 0, 64);
    }

    $key = maca_menulist_attendance_control_session_storage_key($token);
    if ($key === '') {
        return '';
    }

    set_transient(
        $key,
        array(
            'pin_revision' => function_exists('maca_menulist_get_table_order_staff_pin_revision')
                ? maca_menulist_get_table_order_staff_pin_revision()
                : '0',
        ),
        8 * HOUR_IN_SECONDS
    );

    return $token;
}

/**
 * Whether control session token is valid.
 *
 * @param string $token Session token.
 * @return bool
 */
function maca_menulist_validate_attendance_control_session($token) {
    $key = maca_menulist_attendance_control_session_storage_key($token);
    if ($key === '') {
        return false;
    }

    $data = get_transient($key);
    if (!is_array($data)) {
        return false;
    }

    $revision = function_exists('maca_menulist_get_table_order_staff_pin_revision')
        ? maca_menulist_get_table_order_staff_pin_revision()
        : '0';

    return ($data['pin_revision'] ?? '') === $revision;
}

/**
 * Normalize a Swedish personal identity number for storage.
 *
 * @param string $value Raw input.
 * @return string
 */
function maca_menulist_normalize_personal_number($value) {
    $value = sanitize_text_field((string) $value);
    $value = preg_replace('/[^0-9]/', '', $value);

    return is_string($value) ? $value : '';
}

/**
 * Basic validation for stored personal numbers.
 *
 * @param string $value Normalized digits.
 * @return bool
 */
function maca_menulist_is_valid_personal_number($value) {
    $value = maca_menulist_normalize_personal_number($value);
    $length = strlen($value);

    return $length === 10 || $length === 12;
}

/**
 * Mask personal number for display (show last four digits).
 *
 * @param string $value Normalized digits.
 * @return string
 */
function maca_menulist_mask_personal_number($value) {
    $value = maca_menulist_normalize_personal_number($value);
    if ($value === '') {
        return '';
    }

    if (strlen($value) <= 4) {
        return $value;
    }

    return str_repeat('*', strlen($value) - 4) . substr($value, -4);
}

/**
 * Hash an employee PIN.
 *
 * @param string $pin Plain PIN.
 * @return string
 */
function maca_menulist_hash_employee_pin($pin) {
    $pin = preg_replace('/\D/', '', (string) $pin);

    if ($pin === '') {
        return '';
    }

    $prefix = 'hmac:';

    return $prefix . hash_hmac('sha256', $pin, wp_salt('maca_menulist_employee_pin'));
}

/**
 * Verify employee PIN against stored hash.
 *
 * @param string $submitted Submitted PIN.
 * @param string $expected  Stored hash.
 * @return bool
 */
function maca_menulist_verify_employee_pin($submitted, $expected) {
    $expected = (string) $expected;
    if ($expected === '') {
        return false;
    }

    $hash = maca_menulist_hash_employee_pin($submitted);

    return $hash !== '' && hash_equals($expected, $hash);
}

/**
 * Generate a random numeric employee PIN.
 *
 * @return string
 */
function maca_menulist_generate_employee_pin() {
    try {
        $number = random_int(1000, 9999);
    } catch (Throwable $e) {
        $number = wp_rand(1000, 9999);
    }

    return (string) $number;
}

/**
 * Transient key for employee PIN lockout.
 *
 * @return string
 */
function maca_menulist_attendance_pin_lock_key() {
    $ip = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : 'unknown';

    return 'maca_att_pin_lock_' . md5($ip);
}

/**
 * @return array{is_locked: bool, locked_seconds_remaining: int, attempts_left: int, max_attempts: int}
 */
function maca_menulist_get_attendance_pin_feedback() {
    $max_attempts = 5;
    $state = get_transient(maca_menulist_attendance_pin_lock_key());

    if (is_array($state) && !empty($state['locked_until']) && (int) $state['locked_until'] > time()) {
        return array(
            'is_locked' => true,
            'locked_seconds_remaining' => max(0, (int) $state['locked_until'] - time()),
            'attempts_left' => 0,
            'max_attempts' => $max_attempts,
        );
    }

    $failed = is_array($state) ? absint($state['failed'] ?? 0) : 0;

    return array(
        'is_locked' => false,
        'locked_seconds_remaining' => 0,
        'attempts_left' => max(0, $max_attempts - $failed),
        'max_attempts' => $max_attempts,
    );
}

/**
 * Register a failed employee PIN attempt.
 *
 * @return void
 */
function maca_menulist_register_attendance_pin_failed_attempt() {
    $max_attempts = 5;
    $lock_minutes = 15;
    $key = maca_menulist_attendance_pin_lock_key();
    $state = get_transient($key);

    if (!is_array($state)) {
        $state = array('failed' => 0);
    }

    $state['failed'] = absint($state['failed']) + 1;

    if ($state['failed'] >= $max_attempts) {
        $state['locked_until'] = time() + ($lock_minutes * MINUTE_IN_SECONDS);
        $state['failed'] = 0;
    }

    set_transient($key, $state, $lock_minutes * MINUTE_IN_SECONDS);
}

/**
 * Clear employee PIN lockout after success.
 *
 * @return void
 */
function maca_menulist_clear_attendance_pin_lockout() {
    delete_transient(maca_menulist_attendance_pin_lock_key());
}

/**
 * Transient key for an employee session token.
 *
 * @param string $token Session token.
 * @return string
 */
function maca_menulist_employee_session_storage_key($token) {
    $token = strtolower(preg_replace('/[^a-f0-9]/', '', (string) $token));

    if (strlen($token) !== 64) {
        return '';
    }

    return 'maca_att_sess_' . $token;
}

/**
 * Create a session after employee PIN verification.
 *
 * @param int $employee_id Employee ID.
 * @return string Session token.
 */
function maca_menulist_create_employee_session($employee_id) {
    $employee_id = absint($employee_id);
    if ($employee_id <= 0) {
        return '';
    }

    try {
        $token = bin2hex(random_bytes(32));
    } catch (Throwable $e) {
        $token = wp_hash(wp_generate_password(32, true, true) . microtime(true));
        $token = strtolower(preg_replace('/[^a-f0-9]/', '', $token));
        if (strlen($token) < 64) {
            $token = str_pad($token, 64, '0');
        }
        $token = substr($token, 0, 64);
    }

    $key = maca_menulist_employee_session_storage_key($token);
    if ($key === '') {
        return '';
    }

    $client = function_exists('maca_menulist_staff_request_client_fingerprint')
        ? maca_menulist_staff_request_client_fingerprint()
        : '';

    set_transient(
        $key,
        array(
            'employee_id' => $employee_id,
            'client' => $client,
        ),
        12 * HOUR_IN_SECONDS
    );

    return $token;
}

/**
 * Validate employee session token.
 *
 * @param string $token Session token.
 * @return int Employee ID or 0.
 */
function maca_menulist_validate_employee_session($token) {
    $key = maca_menulist_employee_session_storage_key($token);
    if ($key === '') {
        return 0;
    }

    $data = get_transient($key);
    if (!is_array($data)) {
        return 0;
    }

    $client = function_exists('maca_menulist_staff_request_client_fingerprint')
        ? maca_menulist_staff_request_client_fingerprint()
        : '';

    if ($client !== '' && ($data['client'] ?? '') !== $client) {
        return 0;
    }

    return absint($data['employee_id'] ?? 0);
}

/**
 * Whether a self-service request includes employee_id (not allowed).
 *
 * Stamping is tied to the session created by personal PIN only — never a client-supplied id.
 *
 * @return bool
 */
function maca_menulist_attendance_request_has_employee_id_param() {
    // phpcs:ignore WordPress.Security.NonceVerification.Missing
    if (!isset($_POST['employee_id'])) {
        return false;
    }

    // phpcs:ignore WordPress.Security.NonceVerification.Missing
    return absint(wp_unslash($_POST['employee_id'])) > 0;
}

/**
 * Find active employee by personal PIN.
 *
 * @param string $pin Submitted PIN.
 * @return object|null
 */
function maca_menulist_find_employee_by_pin($pin) {
    foreach (maca_menulist_db_get_employees(true) as $employee) {
        if (maca_menulist_verify_employee_pin($pin, (string) $employee->pin_hash)) {
            return $employee;
        }
    }

    return null;
}

/**
 * Current clock status for an employee.
 *
 * @param int $employee_id Employee ID.
 * @return array{is_clocked_in: bool, last_event_type: string, last_occurred_at: string}
 */
function maca_menulist_get_employee_attendance_status($employee_id) {
    $latest = maca_menulist_db_get_latest_attendance_event($employee_id);

    if (!$latest) {
        return array(
            'is_clocked_in' => false,
            'last_event_type' => '',
            'last_occurred_at' => '',
        );
    }

    return array(
        'is_clocked_in' => $latest->event_type === 'clock_in',
        'last_event_type' => (string) $latest->event_type,
        'last_occurred_at' => (string) $latest->occurred_at,
    );
}

/**
 * Format time for attendance UI (site timezone).
 *
 * @param string $mysql_datetime MySQL datetime.
 * @return string
 */
function maca_menulist_format_attendance_time($mysql_datetime) {
    if ($mysql_datetime === '') {
        return '';
    }

    $timestamp = mysql2date('U', $mysql_datetime, false);
    if (!$timestamp) {
        return $mysql_datetime;
    }

    return wp_date(get_option('time_format'), $timestamp);
}

/**
 * Clock an employee in or out.
 *
 * @param int    $employee_id Employee ID.
 * @param string $action      clock_in or clock_out.
 * @return array{success: bool, message: string, event_id?: int}
 */
function maca_menulist_attendance_clock($employee_id, $action) {
    $employee_id = absint($employee_id);
    $action = sanitize_key($action);

    if ($employee_id <= 0 || !in_array($action, array('clock_in', 'clock_out'), true)) {
        return array(
            'success' => false,
            'message' => __('Invalid request.', 'maca-restu-pro'),
        );
    }

    $employee = maca_menulist_db_get_employee($employee_id);
    if (!$employee || !(int) $employee->is_active) {
        return array(
            'success' => false,
            'message' => __('Employee not found.', 'maca-restu-pro'),
        );
    }

    $status = maca_menulist_get_employee_attendance_status($employee_id);

    if ($action === 'clock_in' && $status['is_clocked_in']) {
        return array(
            'success' => false,
            'message' => maca_menulist_attendance_ui_string('You are already clocked in.'),
        );
    }

    if ($action === 'clock_out' && !$status['is_clocked_in']) {
        return array(
            'success' => false,
            'message' => maca_menulist_attendance_ui_string('You are not clocked in.'),
        );
    }

    $now = function_exists('maca_menulist_wp_now_mysql') ? maca_menulist_wp_now_mysql() : current_time('mysql');

    $event_id = maca_menulist_db_insert_attendance_event(
        array(
            'employee_id' => $employee_id,
            'event_type' => $action,
            'occurred_at' => $now,
            'recorded_at' => $now,
            'source' => 'self',
            'created_by' => 0,
        )
    );

    if ($event_id <= 0) {
        return array(
            'success' => false,
            'message' => __('Could not save attendance.', 'maca-restu-pro'),
        );
    }

    maca_menulist_attendance_log_add(
        $event_id,
        $employee_id,
        'created',
        array(
            'actor' => array(
                'type' => 'employee',
                'id' => $employee_id,
                'label' => (string) $employee->name,
            ),
            'summary' => $action === 'clock_in'
                ? __('Clock in', 'maca-restu-pro')
                : __('Clock out', 'maca-restu-pro'),
            'changes' => array(
                array(
                    'field' => 'event_type',
                    'label' => __('Event', 'maca-restu-pro'),
                    'old' => '',
                    'new' => $action === 'clock_in'
                        ? __('Clock in', 'maca-restu-pro')
                        : __('Clock out', 'maca-restu-pro'),
                ),
                array(
                    'field' => 'occurred_at',
                    'label' => __('Time', 'maca-restu-pro'),
                    'old' => '',
                    'new' => maca_menulist_format_attendance_time($now),
                ),
            ),
        )
    );

    return array(
        'success' => true,
        'message' => $action === 'clock_in'
            ? maca_menulist_attendance_ui_string('Clocked in.')
            : maca_menulist_attendance_ui_string('Clocked out.'),
        'event_id' => $event_id,
    );
}

/**
 * Build today's attendance summary per employee.
 *
 * @param string $date Date Y-m-d.
 * @return array<int, array{employee_id: int, name: string, clock_in: string, clock_out: string, is_clocked_in: bool}>
 */
function maca_menulist_build_attendance_day_summary($date = '') {
    if ($date === '') {
        $date = function_exists('maca_menulist_wp_today') ? maca_menulist_wp_today() : wp_date('Y-m-d');
    }

    $employees = array();
    foreach (maca_menulist_db_get_employees(true) as $employee) {
        $employees[(int) $employee->id] = $employee;
    }

    $summary = array();
    foreach ($employees as $employee_id => $employee) {
        $summary[$employee_id] = array(
            'employee_id' => $employee_id,
            'name' => (string) $employee->name,
            'clock_in' => '',
            'clock_out' => '',
            'is_clocked_in' => false,
        );
    }

    foreach (maca_menulist_db_get_attendance_events_for_date($date) as $event) {
        $employee_id = (int) $event->employee_id;
        if (!isset($summary[$employee_id])) {
            continue;
        }

        $time = maca_menulist_format_attendance_time((string) $event->occurred_at);

        if ($event->event_type === 'clock_in') {
            if ($summary[$employee_id]['clock_in'] === '') {
                $summary[$employee_id]['clock_in'] = $time;
            }
            $summary[$employee_id]['is_clocked_in'] = true;
        } elseif ($event->event_type === 'clock_out') {
            $summary[$employee_id]['clock_out'] = $time;
            $summary[$employee_id]['is_clocked_in'] = false;
        }
    }

    return array_values($summary);
}

/**
 * Summarize one calendar day from preloaded events.
 *
 * @param string               $date     Date Y-m-d.
 * @param array<int, object>   $employees Employee rows keyed by ID.
 * @param array<int, object>   $events   Events for the day.
 * @return array<int, array<string, mixed>>
 */
function maca_menulist_summarize_attendance_day_from_events($date, $employees, $events) {
    $summary = array();

    foreach ($employees as $employee_id => $employee) {
        $summary[$employee_id] = array(
            'date' => $date,
            'employee_id' => $employee_id,
            'name' => (string) $employee->name,
            'personal_number' => (string) $employee->personal_number,
            'clock_in' => '',
            'clock_out' => '',
            'is_clocked_in' => false,
            'had_activity' => false,
        );
    }

    foreach ($events as $event) {
        $employee_id = (int) $event->employee_id;
        if (!isset($summary[$employee_id])) {
            continue;
        }

        $time = maca_menulist_format_attendance_time((string) $event->occurred_at);
        $summary[$employee_id]['had_activity'] = true;

        if ($event->event_type === 'clock_in') {
            if ($summary[$employee_id]['clock_in'] === '') {
                $summary[$employee_id]['clock_in'] = $time;
            }
            $summary[$employee_id]['is_clocked_in'] = true;
        } elseif ($event->event_type === 'clock_out') {
            $summary[$employee_id]['clock_out'] = $time;
            $summary[$employee_id]['is_clocked_in'] = false;
        }
    }

    return array_values($summary);
}

/**
 * Build attendance report rows for a date range.
 *
 * @param string $date_from Start date Y-m-d.
 * @param string $date_to   End date Y-m-d.
 * @param array<string, mixed> $args include_inactive, only_with_activity, include_personal_number.
 * @return array<int, array<string, mixed>>
 */
function maca_menulist_build_attendance_report($date_from, $date_to, $args = array()) {
    $date_from = maca_menulist_attendance_normalize_date($date_from);
    $date_to = maca_menulist_attendance_normalize_date($date_to);

    if ($date_from > $date_to) {
        $swap = $date_from;
        $date_from = $date_to;
        $date_to = $swap;
    }

    $include_inactive = !empty($args['include_inactive']);
    $only_with_activity = !empty($args['only_with_activity']);
    $include_personal_number = !empty($args['include_personal_number']);

    $employees = array();
    foreach (maca_menulist_db_get_employees(!$include_inactive) as $employee) {
        $employees[(int) $employee->id] = $employee;
    }

    $events = maca_menulist_db_get_attendance_events_between($date_from, $date_to);
    $events_by_date = array();

    foreach ($events as $event) {
        $event_date = substr((string) $event->occurred_at, 0, 10);
        if (!isset($events_by_date[$event_date])) {
            $events_by_date[$event_date] = array();
        }
        $events_by_date[$event_date][] = $event;
    }

    $rows = array();
    $timezone = wp_timezone();
    $cursor = new DateTimeImmutable($date_from, $timezone);
    $end = new DateTimeImmutable($date_to, $timezone);

    while ($cursor <= $end) {
        $day = $cursor->format('Y-m-d');
        $day_events = isset($events_by_date[$day]) ? $events_by_date[$day] : array();
        $day_rows = maca_menulist_summarize_attendance_day_from_events($day, $employees, $day_events);

        foreach ($day_rows as $row) {
            if ($only_with_activity && empty($row['had_activity'])) {
                continue;
            }

            if ($include_personal_number) {
                $row['personal_number_display'] = maca_menulist_format_personal_number_display((string) $row['personal_number']);
            } else {
                unset($row['personal_number']);
            }

            $rows[] = $row;
        }

        $cursor = $cursor->modify('+1 day');
    }

    return $rows;
}

/**
 * Parse HH:MM on a calendar date to MySQL datetime.
 *
 * @param string $date Date Y-m-d.
 * @param string $time Time HH:MM.
 * @return string Empty string when invalid.
 */
function maca_menulist_attendance_datetime_from_time($date, $time) {
    $date = maca_menulist_attendance_normalize_date($date);
    $time = trim(sanitize_text_field((string) $time));

    if ($time === '') {
        return '';
    }

    if (!preg_match('/^(\d{1,2}):(\d{2})$/', $time, $matches)) {
        return '';
    }

    $hour = (int) $matches[1];
    $minute = (int) $matches[2];

    if ($hour < 0 || $hour > 23 || $minute < 0 || $minute > 59) {
        return '';
    }

    return sprintf('%s %02d:%02d:00', $date, $hour, $minute);
}

/**
 * Extract HH:MM from MySQL datetime for form fields.
 *
 * @param string $mysql_datetime Datetime.
 * @return string
 */
function maca_menulist_attendance_time_input_value($mysql_datetime) {
    if ($mysql_datetime === '') {
        return '';
    }

    $timestamp = mysql2date('U', $mysql_datetime, false);
    if (!$timestamp) {
        return '';
    }

    return wp_date('H:i', $timestamp);
}

/**
 * Find first clock_in and last clock_out events for a day.
 *
 * @param int    $employee_id Employee ID.
 * @param string $date        Date Y-m-d.
 * @return array{clock_in: object|null, clock_out: object|null}
 */
function maca_menulist_attendance_get_day_slot_events($employee_id, $date) {
    $clock_in = null;
    $clock_out = null;

    foreach (maca_menulist_db_get_attendance_events_for_employee_date($employee_id, $date) as $event) {
        if ($event->event_type === 'clock_in' && $clock_in === null) {
            $clock_in = $event;
        }
        if ($event->event_type === 'clock_out') {
            $clock_out = $event;
        }
    }

    return array(
        'clock_in' => $clock_in,
        'clock_out' => $clock_out,
    );
}

/**
 * Log a single attendance field correction.
 *
 * @param int    $event_id     Event ID.
 * @param int    $employee_id  Employee ID.
 * @param string $field_label  Human label.
 * @param string $old_value    Formatted old value.
 * @param string $new_value    Formatted new value.
 * @param string $summary      Optional summary.
 * @return void
 */
function maca_menulist_attendance_log_correction($event_id, $employee_id, $field_label, $old_value, $new_value, $summary = '') {
    if ($old_value === $new_value) {
        return;
    }

    maca_menulist_attendance_log_add(
        $event_id,
        $employee_id,
        'corrected',
        array(
            'summary' => $summary !== '' ? $summary : __('Time corrected', 'maca-restu-pro'),
            'changes' => array(
                array(
                    'field' => sanitize_key(str_replace(' ', '_', strtolower($field_label))),
                    'label' => $field_label,
                    'old' => $old_value,
                    'new' => $new_value,
                ),
            ),
        )
    );
}

/**
 * Apply or create a clock event for one side of a day (in/out).
 *
 * @param int    $employee_id Employee ID.
 * @param string $event_type  clock_in|clock_out.
 * @param string $datetime    MySQL datetime.
 * @param object|null $existing Existing event row.
 * @param int    $admin_id    Correcting user ID.
 * @param string $field_label Label for log.
 * @return array{success: bool, message?: string}
 */
function maca_menulist_attendance_apply_slot_correction($employee_id, $event_type, $datetime, $existing, $admin_id, $field_label) {
    $employee_id = absint($employee_id);
    $admin_id = absint($admin_id);
    $old_label = $existing ? maca_menulist_format_attendance_time((string) $existing->occurred_at) : '';
    $new_label = maca_menulist_format_attendance_time($datetime);

    if ($existing) {
        if ((string) $existing->occurred_at === $datetime) {
            return array('success' => true);
        }

        if (!maca_menulist_db_update_attendance_event_occurred_at((int) $existing->id, $datetime)) {
            return array(
                'success' => false,
                'message' => __('Could not update attendance.', 'maca-restu-pro'),
            );
        }

        maca_menulist_attendance_log_correction(
            (int) $existing->id,
            $employee_id,
            $field_label,
            $old_label,
            $new_label
        );

        return array('success' => true);
    }

    $now = function_exists('maca_menulist_wp_now_mysql') ? maca_menulist_wp_now_mysql() : current_time('mysql');
    $event_id = maca_menulist_db_insert_attendance_event(
        array(
            'employee_id' => $employee_id,
            'event_type' => $event_type,
            'occurred_at' => $datetime,
            'recorded_at' => $now,
            'source' => 'staff',
            'created_by' => $admin_id,
        )
    );

    if ($event_id <= 0) {
        return array(
            'success' => false,
            'message' => __('Could not save attendance.', 'maca-restu-pro'),
        );
    }

    maca_menulist_attendance_log_add(
        $event_id,
        $employee_id,
        'created',
        array(
            'summary' => __('Added by manager', 'maca-restu-pro'),
            'changes' => array(
                array(
                    'field' => sanitize_key($event_type),
                    'label' => $field_label,
                    'old' => '',
                    'new' => $new_label,
                ),
            ),
        )
    );

    return array('success' => true);
}

/**
 * Correct clock-in/out times for one employee on one day (always logged).
 *
 * @param int    $employee_id Employee ID.
 * @param string $date        Date Y-m-d.
 * @param string $clock_in    Time HH:MM or empty to skip.
 * @param string $clock_out   Time HH:MM or empty to skip.
 * @param string $note        Optional note stored in log summary.
 * @return array{success: bool, message: string}
 */
function maca_menulist_attendance_correct_day_times($employee_id, $date, $clock_in, $clock_out, $note = '') {
    $employee_id = absint($employee_id);
    $date = maca_menulist_attendance_normalize_date($date);
    $employee = maca_menulist_db_get_employee($employee_id);

    if (!$employee) {
        return array(
            'success' => false,
            'message' => __('Employee not found.', 'maca-restu-pro'),
        );
    }

    $clock_in = trim((string) $clock_in);
    $clock_out = trim((string) $clock_out);

    if ($clock_in === '' && $clock_out === '') {
        return array(
            'success' => false,
            'message' => __('Enter at least one time to correct.', 'maca-restu-pro'),
        );
    }

    $in_datetime = $clock_in !== '' ? maca_menulist_attendance_datetime_from_time($date, $clock_in) : '';
    $out_datetime = $clock_out !== '' ? maca_menulist_attendance_datetime_from_time($date, $clock_out) : '';

    if ($clock_in !== '' && $in_datetime === '') {
        return array(
            'success' => false,
            'message' => __('Invalid clock-in time.', 'maca-restu-pro'),
        );
    }

    if ($clock_out !== '' && $out_datetime === '') {
        return array(
            'success' => false,
            'message' => __('Invalid clock-out time.', 'maca-restu-pro'),
        );
    }

    if ($in_datetime !== '' && $out_datetime !== '' && $in_datetime >= $out_datetime) {
        return array(
            'success' => false,
            'message' => __('Clock-out must be after clock-in.', 'maca-restu-pro'),
        );
    }

    $slots = maca_menulist_attendance_get_day_slot_events($employee_id, $date);
    $admin_id = get_current_user_id();
    $summary = $note !== '' ? sanitize_text_field($note) : '';

    if ($clock_in !== '') {
        $result = maca_menulist_attendance_apply_slot_correction(
            $employee_id,
            'clock_in',
            $in_datetime,
            $slots['clock_in'],
            $admin_id,
            __('Clock in', 'maca-restu-pro')
        );
        if (!$result['success']) {
            return array(
                'success' => false,
                'message' => $result['message'] ?? __('Could not correct clock-in.', 'maca-restu-pro'),
            );
        }
    }

    if ($clock_out !== '') {
        $result = maca_menulist_attendance_apply_slot_correction(
            $employee_id,
            'clock_out',
            $out_datetime,
            $slots['clock_out'],
            $admin_id,
            __('Clock out', 'maca-restu-pro')
        );
        if (!$result['success']) {
            return array(
                'success' => false,
                'message' => $result['message'] ?? __('Could not correct clock-out.', 'maca-restu-pro'),
            );
        }
    }

    if ($summary !== '' && function_exists('maca_menulist_attendance_log_add')) {
        maca_menulist_attendance_log_add(
            0,
            $employee_id,
            'corrected',
            array(
                'summary' => $summary,
                'changes' => array(),
            )
        );
    }

    return array(
        'success' => true,
        'message' => __('Attendance corrected. Change logged.', 'maca-restu-pro'),
    );
}
