<?php
/**
 * Admin seat availability matrix (times × bookable seats).
 *
 * @package Maca_MenuList
 */

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

/**
 * Build column definitions for the seat matrix on a date.
 *
 * @param string $date Date Y-m-d.
 * @return array<int, array{key: string, label: string, table_id: int, split_slot: string, group: int, seat: int}>
 */
function maca_menulist_build_admin_seat_matrix_columns($date) {
    $date = sanitize_text_field($date);
    $columns = array();

    if ($date === '' || !maca_menulist_table_booking_has_active_tables()) {
        return $columns;
    }

    maca_menulist_db_ensure_table_booking_tables();
    $tables = maca_menulist_db_get_booking_tables(true);

    usort(
        $tables,
        static function ($a, $b) {
            $sort_cmp = (int) $a->sort_order <=> (int) $b->sort_order;
            if ($sort_cmp !== 0) {
                return $sort_cmp;
            }

            return strnatcasecmp((string) $a->table_number, (string) $b->table_number);
        }
    );

    foreach ($tables as $table) {
        $table_id = (int) $table->id;
        $base_number = (string) $table->table_number;
        $split_for_date = maca_menulist_table_is_splittable($table)
            && maca_menulist_is_table_split_for_date($table_id, $date);

        if ($split_for_date) {
            foreach (maca_menulist_get_table_split_slot_letters($table, $date) as $group_index => $slot) {
                $capacity = maca_menulist_table_split_slot_capacity($table, $slot, $date);
                $display_number = maca_menulist_format_split_table_number($base_number, $slot);

                for ($seat = 0; $seat < $capacity; $seat++) {
                    $columns[] = array(
                        'key'        => $table_id . ':' . $slot . ':' . $group_index . ':' . $seat,
                        'label'      => maca_menulist_format_seat_matrix_column_label($display_number, $seat + 1, $capacity),
                        'table_id'   => $table_id,
                        'split_slot' => $slot,
                        'group'      => (int) $group_index,
                        'seat'       => $seat,
                    );
                }
            }
            continue;
        }

        $capacities = maca_menulist_table_is_splittable($table)
            ? maca_menulist_get_table_split_capacities($table, $date)
            : array( max(1, (int) $table->seats) );

        foreach ($capacities as $group_index => $capacity) {
            $capacity = max(0, (int) $capacity);

            for ($seat = 0; $seat < $capacity; $seat++) {
                $columns[] = array(
                    'key'        => $table_id . '::' . $group_index . ':' . $seat,
                    'label'      => maca_menulist_format_seat_matrix_column_label($base_number, $seat + 1, $capacity),
                    'table_id'   => $table_id,
                    'split_slot' => '',
                    'group'      => (int) $group_index,
                    'seat'       => $seat,
                );
            }
        }
    }

    return $columns;
}

/**
 * Column header label for one bookable seat.
 *
 * @param string $table_number Display table number.
 * @param int    $seat_index   1-based seat index within the part.
 * @param int    $capacity     Seats in this part.
 * @return string
 */
function maca_menulist_format_seat_matrix_column_label($table_number, $seat_index, $capacity) {
    if ($capacity <= 1) {
        return $table_number;
    }

    return sprintf(
        /* translators: 1: table number, 2: seat number */
        __('%1$s · %2$d', 'maca-restu-pro'),
        $table_number,
        (int) $seat_index
    );
}

/**
 * Flatten floor seat_slots to per-seat busy flags (same order as matrix columns).
 *
 * @param array<int, array{capacity: int, occupied: int}> $seat_slots Seat groups.
 * @return array<int, bool> true = occupied.
 */
function maca_menulist_flatten_floor_seat_busy_flags(array $seat_slots) {
    $flags = array();

    foreach ($seat_slots as $slot) {
        $capacity = max(0, (int) $slot['capacity']);
        $occupied = max(0, min($capacity, (int) $slot['occupied']));

        for ($i = 0; $i < $capacity; $i++) {
            $flags[] = ($i < $occupied);
        }
    }

    return $flags;
}

/**
 * Seat busy flags for one floor row (table or split part).
 *
 * @param object $table      Table row.
 * @param string $date       Date Y-m-d.
 * @param string $time_norm  Time HH:MM.
 * @param string $split_slot Slot letter or empty.
 * @return array<int, bool>
 */
function maca_menulist_get_admin_seat_busy_flags_for_row($table, $date, $time_norm, $split_slot = '') {
    $row = maca_menulist_build_admin_floor_table_row($table, $date, $time_norm, $split_slot);
    $seat_slots = isset($row['seat_slots']) && is_array($row['seat_slots']) ? $row['seat_slots'] : array();

    return maca_menulist_flatten_floor_seat_busy_flags($seat_slots);
}

/**
 * Full seat matrix payload for admin (one date, all time slots × seats).
 *
 * @param string $date Date Y-m-d.
 * @return array<string, mixed>
 */
function maca_menulist_get_admin_booking_seat_matrix($date) {
    static $cache = array();

    $date = sanitize_text_field($date);

    if ($date !== '' && isset($cache[ $date ])) {
        return $cache[ $date ];
    }

    if ($date === '') {
        return array(
            'closed'  => true,
            'message' => __('Invalid date.', 'maca-restu-pro'),
            'columns' => array(),
            'times'   => array(),
            'cells'   => array(),
        );
    }

    if (!maca_menulist_table_booking_has_active_tables()) {
        return array(
            'closed'  => true,
            'message' => __('No tables are configured yet.', 'maca-restu-pro'),
            'columns' => array(),
            'times'   => array(),
            'cells'   => array(),
        );
    }

    $hours = maca_menulist_get_booking_hours_for_date($date);
    if (!maca_menulist_hours_are_bookable($hours)) {
        return array(
            'closed'  => true,
            'message' => maca_menulist_table_booking_get_unavailable_message($date, 1),
            'columns' => array(),
            'times'   => array(),
            'cells'   => array(),
        );
    }

    $times = maca_menulist_get_admin_booking_time_slots($date);
    $columns = maca_menulist_build_admin_seat_matrix_columns($date);

    if (empty($times) || empty($columns)) {
        return array(
            'closed'  => true,
            'message' => __('No bookable time slots on this date.', 'maca-restu-pro'),
            'columns' => $columns,
            'times'   => $times,
            'cells'   => array(),
        );
    }

    maca_menulist_db_ensure_table_booking_tables();
    $tables_by_id = array();

    foreach (maca_menulist_db_get_booking_tables(true) as $table) {
        $tables_by_id[ (int) $table->id ] = $table;
    }

    $row_cache = array();
    $cells = array();

    foreach ($times as $time) {
        $time_norm = maca_menulist_normalize_time($time);
        if ($time_norm === null) {
            continue;
        }

        // Cell keys must match $times (HH:MM); normalize_time() returns HH:MM:SS for overlap checks only.
        $time_key = preg_match('/^\d{2}:\d{2}$/', (string) $time)
            ? (string) $time
            : substr($time_norm, 0, 5);

        $cells[ $time_key ] = array();
        $part_seat_index = array();

        foreach ($columns as $column) {
            $table_id = (int) $column['table_id'];
            $split_slot = maca_menulist_normalize_split_slot((string) $column['split_slot']);
            $row_key = $table_id . '|' . $split_slot . '|' . $time_norm;
            $part_key = $table_id . '|' . $split_slot;

            if (!isset($row_cache[ $row_key ])) {
                $table = $tables_by_id[ $table_id ] ?? null;
                $row_cache[ $row_key ] = ($table instanceof stdClass)
                    ? maca_menulist_get_admin_seat_busy_flags_for_row($table, $date, $time_norm, $split_slot)
                    : array();
            }

            if (!isset($part_seat_index[ $part_key ])) {
                $part_seat_index[ $part_key ] = 0;
            }

            $flags = $row_cache[ $row_key ];
            $idx = (int) $part_seat_index[ $part_key ];
            $cells[ $time_key ][ $column['key'] ] = !empty($flags[ $idx ]) ? 'busy' : 'free';
            $part_seat_index[ $part_key ] = $idx + 1;
        }
    }

    $payload_columns = array();

    foreach ($columns as $column) {
        $payload_columns[] = array(
            'key'   => $column['key'],
            'label' => $column['label'],
        );
    }

    $cache[ $date ] = array(
        'closed'  => false,
        'message' => '',
        'columns' => $payload_columns,
        'times'   => array_values($times),
        'cells'   => $cells,
    );

    return $cache[ $date ];
}

/**
 * Seat utilization for a date (same rules as the admin seat matrix).
 *
 * Peak capacity = bookable seats × sittings, where sittings = opening hours ÷ booking duration.
 * Occupancy = total guests on blocking bookings (confirmed + pending) vs peak capacity.
 *
 * @param string $date Date Y-m-d.
 * @return array{
 *   seats_per_sitting: int,
 *   sitting_count: int,
 *   peak_capacity: int,
 *   booked_seats: int,
 *   remaining_seats: int,
 *   max_free_seats: int,
 *   daily_occupancy_percent: int
 * }
 */
function maca_menulist_get_date_seat_utilization_summary($date) {
    $date = sanitize_text_field($date);
    $empty = array(
        'seats_per_sitting'         => 0,
        'sitting_count'             => 0,
        'peak_capacity'             => 0,
        'booked_seats'              => 0,
        'remaining_seats'           => 0,
        'max_free_seats'            => 0,
        'daily_occupancy_percent'   => 0,
    );

    if ($date === '' || !maca_menulist_table_booking_has_active_tables()) {
        return $empty;
    }

    $matrix = maca_menulist_get_admin_booking_seat_matrix($date);
    $seats_per_sitting = is_array($matrix['columns']) ? count($matrix['columns']) : 0;
    $times = (array) ($matrix['times'] ?? array());
    $sitting_count = function_exists('maca_menulist_get_date_booking_sitting_count')
        ? maca_menulist_get_date_booking_sitting_count($date)
        : 0;

    if (!empty($matrix['closed']) || $seats_per_sitting === 0 || $sitting_count === 0) {
        return $empty;
    }

    $daily_capacity = $seats_per_sitting * $sitting_count;
    $day_stats = maca_menulist_db_get_booking_day_stats($date);
    $booked_seats = (int) $day_stats['confirmed_guests'] + (int) $day_stats['pending_guests'];
    $booked_seats = min($booked_seats, $daily_capacity);
    $remaining_seats = max(0, $daily_capacity - $booked_seats);

    $max_free_slot = 0;

    foreach ($times as $time) {
        $time_key = (string) $time;

        if (!preg_match('/^\d{2}:\d{2}$/', $time_key)) {
            $time_key = substr($time_key, 0, 5);
        }

        $cells = isset($matrix['cells'][ $time_key ]) && is_array($matrix['cells'][ $time_key ])
            ? $matrix['cells'][ $time_key ]
            : array();

        $busy = 0;

        foreach ($cells as $state) {
            if ($state === 'busy') {
                $busy++;
            }
        }

        $busy = min($busy, $seats_per_sitting);
        $free_slot = $seats_per_sitting - $busy;

        if ($free_slot > $max_free_slot) {
            $max_free_slot = $free_slot;
        }
    }

    $daily_occupancy_percent = $daily_capacity > 0
        ? (int) min(100, (int) round(($booked_seats / $daily_capacity) * 100))
        : 0;

    return array(
        'seats_per_sitting'       => $seats_per_sitting,
        'sitting_count'           => $sitting_count,
        'peak_capacity'           => $daily_capacity,
        'booked_seats'            => $booked_seats,
        'remaining_seats'         => $remaining_seats,
        'max_free_seats'          => $max_free_slot,
        'daily_occupancy_percent' => $daily_occupancy_percent,
    );
}
