<?php
/**
 * Audit log for table reservations (staff disputes, traceability).
 *
 * @package Maca_MenuList
 */

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

/**
 * Booking log table name.
 *
 * @return string
 */
function maca_menulist_db_booking_log_table() {
    global $wpdb;

    return $wpdb->prefix . 'maca_booking_log';
}

/**
 * Create booking log table.
 *
 * @return void
 */
function maca_menulist_db_create_booking_log_table() {
    global $wpdb;

    if (!isset($wpdb) || empty($wpdb)) {
        return;
    }

    $table = maca_menulist_db_booking_log_table();
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table (
        id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
        booking_id bigint(20) unsigned NOT NULL DEFAULT 0,
        action varchar(32) NOT NULL DEFAULT 'updated',
        summary text DEFAULT NULL,
        changes longtext DEFAULT NULL,
        actor_type varchar(16) NOT NULL DEFAULT 'system',
        actor_id bigint(20) unsigned NOT NULL DEFAULT 0,
        actor_label varchar(255) DEFAULT NULL,
        created_at datetime DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY  (id),
        KEY booking_id (booking_id),
        KEY created_at (created_at)
    ) $charset_collate;";

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    dbDelta($sql);
}

/**
 * Ensure booking log table exists.
 *
 * @return void
 */
function maca_menulist_db_ensure_booking_log_table() {
    global $wpdb;

    static $ensured = false;
    if ($ensured) {
        return;
    }

    $table = maca_menulist_db_booking_log_table();
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
    if ($exists !== $table) {
        maca_menulist_db_create_booking_log_table();
    }

    $ensured = true;
}

/**
 * Current actor for log entries.
 *
 * @return array{type: string, id: int, label: string}
 */
function maca_menulist_booking_log_current_actor() {
    if (is_user_logged_in()) {
        $user = wp_get_current_user();
        $label = $user->display_name !== '' ? $user->display_name : $user->user_login;

        return array(
            'type' => 'staff',
            'id' => (int) $user->ID,
            'label' => $label,
        );
    }

    return array(
        'type' => 'guest',
        'id' => 0,
        'label' => __('Gäst', 'maca-restu-pro'),
    );
}

/**
 * Human-readable booking field labels.
 *
 * @return array<string, string>
 */
function maca_menulist_booking_log_field_labels() {
    return array(
        'guest_name' => __('Namn', 'maca-restu-pro'),
        'guest_phone' => __('Telefon', 'maca-restu-pro'),
        'guest_email' => __('E-post', 'maca-restu-pro'),
        'guest_note' => __('Anteckning', 'maca-restu-pro'),
        'booking_date' => __('Datum', 'maca-restu-pro'),
        'booking_time' => __('Tid', 'maca-restu-pro'),
        'party_size' => __('Antal gäster', 'maca-restu-pro'),
        'status' => __('Status', 'maca-restu-pro'),
        'table_id' => __('Bord', 'maca-restu-pro'),
        'split_slot' => __('Del av bord', 'maca-restu-pro'),
    );
}

/**
 * Format a value for the log display.
 *
 * @param string $field Field key.
 * @param mixed  $value Raw value.
 * @return string
 */
function maca_menulist_booking_log_format_value($field, $value) {
    if ($field === 'guest_email' && function_exists('maca_menulist_staff_booking_placeholder_email')) {
        if ((string) $value === maca_menulist_staff_booking_placeholder_email()) {
            return '—';
        }
    }

    if ($field === 'status' && function_exists('maca_menulist_get_booking_status_label')) {
        return maca_menulist_get_booking_status_label((string) $value);
    }

    if ($field === 'table_id') {
        $table_id = absint($value);
        if ($table_id <= 0) {
            return __('Inte tilldelad', 'maca-restu-pro');
        }
        if (function_exists('maca_menulist_db_get_booking_table')) {
            $table = maca_menulist_db_get_booking_table($table_id);
            if ($table && !empty($table->table_number)) {
                return (string) $table->table_number;
            }
        }

        return '#' . $table_id;
    }

    if ($field === 'booking_time') {
        return substr((string) $value, 0, 5);
    }

    return (string) $value;
}

/**
 * Insert a booking log row.
 *
 * @param int                  $booking_id Booking ID.
 * @param string               $action     Action code.
 * @param array<string, mixed> $args       Optional summary, changes, actor.
 * @return bool
 */
function maca_menulist_booking_log_add($booking_id, $action, $args = array()) {
    global $wpdb;

    maca_menulist_db_ensure_booking_log_table();

    $booking_id = absint($booking_id);
    $action = sanitize_key($action);
    if ($booking_id <= 0 || $action === '') {
        return false;
    }

    $actor = isset($args['actor']) && is_array($args['actor']) ? $args['actor'] : maca_menulist_booking_log_current_actor();
    $summary = isset($args['summary']) ? sanitize_text_field((string) $args['summary']) : '';
    $changes = isset($args['changes']) && is_array($args['changes']) ? $args['changes'] : array();

    $changes_json = $changes !== array() ? wp_json_encode($changes, JSON_UNESCAPED_UNICODE) : null;

    $inserted = $wpdb->insert(
        maca_menulist_db_booking_log_table(),
        array(
            'booking_id' => $booking_id,
            'action' => $action,
            'summary' => $summary !== '' ? $summary : null,
            'changes' => $changes_json,
            'actor_type' => sanitize_key((string) ($actor['type'] ?? 'system')),
            'actor_id' => absint($actor['id'] ?? 0),
            'actor_label' => isset($actor['label']) ? sanitize_text_field((string) $actor['label']) : null,
            'created_at' => maca_menulist_wp_now_mysql(),
        ),
        array('%d', '%s', '%s', '%s', '%s', '%d', '%s', '%s')
    );

    return (bool) $inserted;
}

/**
 * Build change rows from old/new field maps.
 *
 * @param array<string, mixed> $before Before values.
 * @param array<string, mixed> $after  After values.
 * @return array<int, array{field: string, label: string, old: string, new: string}>
 */
function maca_menulist_booking_log_diff_fields($before, $after) {
    $labels = maca_menulist_booking_log_field_labels();
    $changes = array();

    foreach ($after as $field => $new_value) {
        if (!isset($labels[$field])) {
            continue;
        }

        $old_value = isset($before[$field]) ? $before[$field] : '';
        $old_fmt = maca_menulist_booking_log_format_value($field, $old_value);
        $new_fmt = maca_menulist_booking_log_format_value($field, $new_value);

        if ($old_fmt === $new_fmt) {
            continue;
        }

        $changes[] = array(
            'field' => $field,
            'label' => $labels[$field],
            'old' => $old_fmt,
            'new' => $new_fmt,
        );
    }

    return $changes;
}

/**
 * Log field updates on a booking.
 *
 * @param int                  $booking_id Booking ID.
 * @param array<string, mixed> $before     Previous values (subset).
 * @param array<string, mixed> $after      New values (subset).
 * @param string               $action     Action code.
 * @return void
 */
function maca_menulist_booking_log_field_updates($booking_id, $before, $after, $action = 'updated') {
    $changes = maca_menulist_booking_log_diff_fields($before, $after);
    if ($changes === array()) {
        return;
    }

    maca_menulist_booking_log_add(
        $booking_id,
        $action,
        array(
            'changes' => $changes,
        )
    );
}

/**
 * Log booking creation.
 *
 * @param int    $booking_id Booking ID.
 * @param string $source     Source label (guest, staff, …).
 * @return void
 */
function maca_menulist_booking_log_created($booking_id, $source = 'staff') {
    $source = sanitize_key($source);
    $labels = array(
        'guest' => __('Bokning skapad av gäst online.', 'maca-restu-pro'),
        'staff' => __('Bokning skapad av personal.', 'maca-restu-pro'),
    );

    maca_menulist_booking_log_add(
        $booking_id,
        'created',
        array(
            'summary' => isset($labels[$source]) ? $labels[$source] : $labels['staff'],
        )
    );
}

/**
 * Allowed page sizes for the admin log list.
 *
 * @return array<int, int>
 */
function maca_menulist_booking_log_per_page_options() {
    return array(50, 100, 200, 500);
}

/**
 * Normalize per-page value for log queries.
 *
 * @param int $per_page Requested page size.
 * @return int
 */
function maca_menulist_booking_log_normalize_per_page($per_page) {
    $per_page = absint($per_page);
    $allowed = maca_menulist_booking_log_per_page_options();

    if (in_array($per_page, $allowed, true)) {
        return $per_page;
    }

    return 100;
}

/**
 * Count log entries (for pagination).
 *
 * @param array<string, mixed> $args Query args (booking_id).
 * @return int
 */
function maca_menulist_booking_log_count($args = array()) {
    global $wpdb;

    maca_menulist_db_ensure_booking_log_table();

    $booking_id = isset($args['booking_id']) ? absint($args['booking_id']) : 0;
    $log_table = maca_menulist_db_booking_log_table();

    if ($booking_id > 0) {
        return (int) $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM $log_table WHERE booking_id = %d",
                $booking_id
            )
        );
    }

    return (int) $wpdb->get_var("SELECT COUNT(*) FROM $log_table");
}

/**
 * List log entries.
 *
 * @param array<string, mixed> $args Query args.
 * @return array<int, object>
 */
function maca_menulist_booking_log_list($args = array()) {
    global $wpdb;

    maca_menulist_db_ensure_booking_log_table();

    $defaults = array(
        'booking_id' => 0,
        'per_page' => 100,
        'paged' => 1,
    );
    $args = wp_parse_args($args, $defaults);

    $per_page = maca_menulist_booking_log_normalize_per_page((int) $args['per_page']);
    $paged = max(1, (int) $args['paged']);
    $offset = ($paged - 1) * $per_page;
    $booking_id = absint($args['booking_id']);

    $log_table = maca_menulist_db_booking_log_table();
    $bookings_table = maca_menulist_db_bookings_table();

    if ($booking_id > 0) {
        return $wpdb->get_results(
            $wpdb->prepare(
                "SELECT l.* FROM $log_table l
                WHERE l.booking_id = %d
                ORDER BY l.created_at DESC, l.id DESC
                LIMIT %d OFFSET %d",
                $booking_id,
                $per_page,
                $offset
            )
        );
    }

    return $wpdb->get_results(
        $wpdb->prepare(
            "SELECT l.*, b.guest_name, b.booking_date, b.booking_time
            FROM $log_table l
            LEFT JOIN $bookings_table b ON b.id = l.booking_id
            ORDER BY l.created_at DESC, l.id DESC
            LIMIT %d OFFSET %d",
            $per_page,
            $offset
        )
    );
}

/**
 * Build admin URL for the booking log tab.
 *
 * @param array<string, int|string> $args Query args.
 * @return string
 */
function maca_menulist_booking_log_admin_url($args = array()) {
    $query = array_merge(
        array(
            'page' => 'maca-restu-bookings',
            'tab' => 'log',
        ),
        $args
    );

    return add_query_arg($query, admin_url('admin.php'));
}

/**
 * Action label for log list.
 *
 * @param string $action Action code.
 * @return string
 */
function maca_menulist_booking_log_action_label($action) {
    $labels = array(
        'created' => __('Skapad', 'maca-restu-pro'),
        'updated' => __('Uppdaterad', 'maca-restu-pro'),
        'confirmed' => __('Bekräftad', 'maca-restu-pro'),
        'cancelled' => __('Avbokad', 'maca-restu-pro'),
        'deleted' => __('Raderad', 'maca-restu-pro'),
        'moved' => __('Flyttad', 'maca-restu-pro'),
        'waitlist_placed' => __('Väntlista → placerad', 'maca-restu-pro'),
        'email' => __('E-post', 'maca-restu-pro'),
    );

    $action = sanitize_key($action);

    return isset($labels[$action]) ? $labels[$action] : $action;
}

/**
 * Render change details HTML for one log row.
 *
 * @param object $entry Log row.
 * @return string
 */
function maca_menulist_booking_log_format_changes_html($entry) {
    if (empty($entry->changes)) {
        return $entry->summary !== null && $entry->summary !== ''
            ? esc_html((string) $entry->summary)
            : '';
    }

    $decoded = json_decode((string) $entry->changes, true);
    if (!is_array($decoded) || $decoded === array()) {
        return $entry->summary !== null && $entry->summary !== ''
            ? esc_html((string) $entry->summary)
            : '';
    }

    $parts = array();
    foreach ($decoded as $change) {
        if (!is_array($change) || empty($change['label'])) {
            continue;
        }
        $parts[] = sprintf(
            '<strong>%1$s:</strong> %2$s → %3$s',
            esc_html((string) $change['label']),
            esc_html((string) ($change['old'] ?? '')),
            esc_html((string) ($change['new'] ?? ''))
        );
    }

    if ($parts === array()) {
        return $entry->summary !== null && $entry->summary !== ''
            ? esc_html((string) $entry->summary)
            : '';
    }

    return implode('<br>', $parts);
}
