<?php
/**
 * Monthly personalliggare archive (CSV + printable HTML on server).
 *
 * @package Maca_Menu_List
 */

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

if (!defined('MACA_MENULIST_ATTENDANCE_ARCHIVE_CRON_HOOK')) {
    define('MACA_MENULIST_ATTENDANCE_ARCHIVE_CRON_HOOK', 'maca_menulist_attendance_archive_cron');
}

/**
 * Whether automatic monthly archiving is enabled.
 *
 * @return bool
 */
function maca_menulist_attendance_archive_enabled() {
    return get_option('maca_menulist_attendance_archive_enabled', '1') === '1';
}

/**
 * How many months of archives to keep (Skatteverket: at least 2 years).
 *
 * @return int
 */
function maca_menulist_attendance_archive_get_retention_months() {
    $months = (int) get_option('maca_menulist_attendance_archive_retention_months', 24);

    return max(12, min(120, $months));
}

/**
 * Run time on the 1st of each month (HH:MM, site timezone).
 *
 * @return string
 */
function maca_menulist_attendance_archive_get_run_time() {
    $time = get_option('maca_menulist_attendance_archive_time', '03:00');
    if (!is_string($time) || !preg_match('/^\d{2}:\d{2}$/', $time)) {
        return '03:00';
    }

    return $time;
}

/**
 * Archive storage directory (not web-accessible).
 *
 * @return string
 */
function maca_menulist_attendance_archive_get_directory() {
    $upload = wp_upload_dir();
    $dir = trailingslashit($upload['basedir']) . 'maca-menulist-attendance';

    if (!file_exists($dir)) {
        wp_mkdir_p($dir);
    }

    if (file_exists($dir)) {
        $index = trailingslashit($dir) . 'index.html';
        if (!file_exists($index)) {
            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
            file_put_contents($index, '');
        }

        $htaccess = trailingslashit($dir) . '.htaccess';
        if (!file_exists($htaccess)) {
            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
            file_put_contents(
                $htaccess,
                "Options -Indexes\n<FilesMatch \"\\.(csv|html)$\">\n  Require all denied\n</FilesMatch>\n"
            );
        }
    }

    return $dir;
}

/**
 * Saved archive index keyed by Y-m.
 *
 * @return array<string, array<string, mixed>>
 */
function maca_menulist_attendance_archive_get_index() {
    $index = get_option('maca_menulist_attendance_archive_index', array());

    return is_array($index) ? $index : array();
}

/**
 * Persist archive index.
 *
 * @param array<string, array<string, mixed>> $index Index.
 * @return void
 */
function maca_menulist_attendance_archive_save_index($index) {
    if (!is_array($index)) {
        $index = array();
    }

    krsort($index);
    update_option('maca_menulist_attendance_archive_index', $index, false);
}

/**
 * Validate month key Y-m.
 *
 * @param string $year_month Month key.
 * @return string Empty when invalid.
 */
function maca_menulist_attendance_archive_normalize_month($year_month) {
    $year_month = sanitize_text_field((string) $year_month);

    if (!preg_match('/^\d{4}-\d{2}$/', $year_month)) {
        return '';
    }

    $parts = explode('-', $year_month);
    $month = (int) $parts[1];

    if ($month < 1 || $month > 12) {
        return '';
    }

    return $year_month;
}

/**
 * First and last calendar day for a month key.
 *
 * @param string $year_month Month Y-m.
 * @return array{date_from: string, date_to: string, label: string}
 */
function maca_menulist_attendance_archive_month_range($year_month) {
    $year_month = maca_menulist_attendance_archive_normalize_month($year_month);
    $timezone = wp_timezone();
    $start = DateTimeImmutable::createFromFormat('Y-m-d', $year_month . '-01', $timezone);

    if (!$start) {
        $today = wp_date('Y-m-d');
        return array(
            'date_from' => $today,
            'date_to' => $today,
            'label' => $today,
        );
    }

    $end = $start->modify('last day of this month');

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

    $label = wp_date('F Y', $start->getTimestamp());

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

    return array(
        'date_from' => $start->format('Y-m-d'),
        'date_to' => $end->format('Y-m-d'),
        'label' => $label,
    );
}

/**
 * Previous complete calendar month key.
 *
 * @return string Y-m
 */
function maca_menulist_attendance_archive_previous_month_key() {
    $timezone = wp_timezone();
    $now = new DateTimeImmutable('now', $timezone);

    return $now->modify('first day of last month')->format('Y-m');
}

/**
 * Build base filename for a month archive.
 *
 * @param string $year_month Month Y-m.
 * @return string
 */
function maca_menulist_attendance_archive_base_filename($year_month) {
    return 'personalliggare-' . maca_menulist_attendance_archive_normalize_month($year_month);
}

/**
 * Write CSV report rows to a file.
 *
 * @param string               $path File path.
 * @param array<int, array<string, mixed>> $rows Report rows.
 * @return bool
 */
function maca_menulist_attendance_archive_write_csv_file($path, $rows) {
    // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- Local archive CSV write.
    $handle = fopen($path, 'wb');
    if ($handle === false) {
        return false;
    }

    fprintf($handle, chr(0xEF) . chr(0xBB) . chr(0xBF));

    fputcsv(
        $handle,
        array(
            maca_menulist_attendance_ui_string('Date'),
            maca_menulist_attendance_ui_string('Name'),
            maca_menulist_attendance_ui_string('Personal identity number'),
            maca_menulist_attendance_ui_string('In'),
            maca_menulist_attendance_ui_string('Out'),
            maca_menulist_attendance_ui_string('Status'),
        )
    );

    foreach ($rows as $row) {
        fputcsv(
            $handle,
            array(
                (string) $row['date'],
                (string) $row['name'],
                (string) ($row['personal_number_display'] ?? ''),
                (string) $row['clock_in'],
                (string) $row['clock_out'],
                !empty($row['is_clocked_in'])
                    ? maca_menulist_attendance_ui_string('Clocked in')
                    : (!empty($row['had_activity']) ? maca_menulist_attendance_ui_string('Clocked out') : maca_menulist_attendance_ui_string('No activity')),
            )
        );
    }

    // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Local archive CSV write.
    fclose($handle);

    return true;
}

/**
 * Build printable HTML for a monthly report.
 *
 * @param array<int, array<string, mixed>> $rows Report rows.
 * @param string                           $period_label Human-readable period.
 * @param string                           $generated_at ISO timestamp.
 * @return string
 */
function maca_menulist_attendance_archive_build_html($rows, $period_label, $generated_at) {
    $site_name = get_bloginfo('name');
    $title = sprintf(
        maca_menulist_attendance_ui_string('Attendance report — %s'),
        $period_label
    );

    ob_start();
    ?>
<!DOCTYPE html>
<html lang="<?php echo esc_attr(maca_menulist_attendance_html_lang()); ?>">
<head>
    <meta charset="utf-8">
    <title><?php echo esc_html($title); ?></title>
    <style>
        body { font-family: system-ui, sans-serif; font-size: 12px; color: #111; margin: 24px; }
        h1 { font-size: 18px; margin: 0 0 4px; }
        .meta { color: #444; margin-bottom: 20px; }
        table { width: 100%; border-collapse: collapse; }
        th, td { border: 1px solid #ccc; padding: 6px 8px; text-align: left; vertical-align: top; }
        th { background: #f3f4f6; }
        @media print {
            body { margin: 12px; }
            .no-print { display: none; }
        }
    </style>
</head>
<body>
    <h1><?php echo esc_html($title); ?></h1>
    <p class="meta">
        <?php echo esc_html($site_name); ?><br>
        <?php
        printf(
            esc_html(maca_menulist_attendance_ui_string('Generated: %s')),
            esc_html(maca_menulist_attendance_format_datetime($generated_at))
        );
        ?>
    </p>
    <table>
        <thead>
            <tr>
                <th><?php echo esc_html(maca_menulist_attendance_ui_string('Date')); ?></th>
                <th><?php echo esc_html(maca_menulist_attendance_ui_string('Name')); ?></th>
                <th><?php echo esc_html(maca_menulist_attendance_ui_string('Personal identity number')); ?></th>
                <th><?php echo esc_html(maca_menulist_attendance_ui_string('In')); ?></th>
                <th><?php echo esc_html(maca_menulist_attendance_ui_string('Out')); ?></th>
                <th><?php echo esc_html(maca_menulist_attendance_ui_string('Status')); ?></th>
            </tr>
        </thead>
        <tbody>
            <?php if ($rows === array()) : ?>
                <tr>
                    <td colspan="6"><?php echo esc_html(maca_menulist_attendance_ui_string('No rows in this period.')); ?></td>
                </tr>
            <?php else : ?>
                <?php foreach ($rows as $row) : ?>
                    <tr>
                        <td><?php echo esc_html((string) $row['date']); ?></td>
                        <td><?php echo esc_html((string) $row['name']); ?></td>
                        <td><?php echo esc_html((string) ($row['personal_number_display'] ?? '')); ?></td>
                        <td><?php echo esc_html((string) $row['clock_in']); ?></td>
                        <td><?php echo esc_html((string) $row['clock_out']); ?></td>
                        <td>
                            <?php
                            if (!empty($row['is_clocked_in'])) {
                                echo esc_html(maca_menulist_attendance_ui_string('Clocked in'));
                            } elseif (!empty($row['had_activity'])) {
                                echo esc_html(maca_menulist_attendance_ui_string('Clocked out'));
                            } else {
                                echo esc_html(maca_menulist_attendance_ui_string('No activity'));
                            }
                            ?>
                        </td>
                    </tr>
                <?php endforeach; ?>
            <?php endif; ?>
        </tbody>
    </table>
    <p class="no-print" style="margin-top: 24px;">
        <button type="button" onclick="window.print();"><?php echo esc_html(maca_menulist_attendance_ui_string('Print')); ?></button>
    </p>
</body>
</html>
    <?php
    return (string) ob_get_clean();
}

/**
 * Create or replace archive files for one month.
 *
 * @param string $year_month Month Y-m.
 * @param string $source     cron|manual.
 * @return array{success: bool, message: string, month: string}
 */
function maca_menulist_attendance_archive_create_month($year_month, $source = 'cron') {
    $year_month = maca_menulist_attendance_archive_normalize_month($year_month);
    if ($year_month === '') {
        return array(
            'success' => false,
            'message' => __('Invalid month.', 'maca-restu-pro'),
            'month' => '',
        );
    }

    if (!function_exists('maca_menulist_build_attendance_report')) {
        return array(
            'success' => false,
            'message' => __('Attendance report is not available.', 'maca-restu-pro'),
            'month' => $year_month,
        );
    }

    $range = maca_menulist_attendance_archive_month_range($year_month);
    $rows = maca_menulist_build_attendance_report(
        $range['date_from'],
        $range['date_to'],
        array(
            'include_personal_number' => true,
            'only_with_activity' => false,
        )
    );

    $dir = maca_menulist_attendance_archive_get_directory();
    $base = maca_menulist_attendance_archive_base_filename($year_month);
    $csv_name = $base . '.csv';
    $html_name = $base . '.html';
    $csv_path = trailingslashit($dir) . $csv_name;
    $html_path = trailingslashit($dir) . $html_name;

    if (!maca_menulist_attendance_archive_write_csv_file($csv_path, $rows)) {
        return array(
            'success' => false,
            'message' => __('Could not write CSV archive.', 'maca-restu-pro'),
            'month' => $year_month,
        );
    }

    $generated_at = wp_date('Y-m-d H:i:s');
    $html = maca_menulist_attendance_archive_build_html($rows, $range['label'], $generated_at);
    // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
    if (file_put_contents($html_path, $html) === false) {
        wp_delete_file($csv_path);

        return array(
            'success' => false,
            'message' => __('Could not write printable archive.', 'maca-restu-pro'),
            'month' => $year_month,
        );
    }

    $index = maca_menulist_attendance_archive_get_index();
    $index[$year_month] = array(
        'month' => $year_month,
        'label' => $range['label'],
        'date_from' => $range['date_from'],
        'date_to' => $range['date_to'],
        'csv' => $csv_name,
        'html' => $html_name,
        'row_count' => count($rows),
        'created_at' => $generated_at,
        'source' => sanitize_key($source),
    );
    maca_menulist_attendance_archive_save_index($index);

    update_option('maca_menulist_attendance_archive_last_run', $generated_at);
    update_option('maca_menulist_attendance_archive_last_month', $year_month);

    maca_menulist_attendance_archive_purge_old();

    return array(
        'success' => true,
        'message' => sprintf(
            /* translators: %s: month label */
            __('Monthly archive saved for %s.', 'maca-restu-pro'),
            $range['label']
        ),
        'month' => $year_month,
    );
}

/**
 * Delete archives older than retention.
 *
 * @return int Number of months removed.
 */
function maca_menulist_attendance_archive_purge_old() {
    $retention = maca_menulist_attendance_archive_get_retention_months();
    $timezone = wp_timezone();
    $cutoff = (new DateTimeImmutable('now', $timezone))
        ->modify('first day of this month')
        ->modify('-' . $retention . ' months');

    $index = maca_menulist_attendance_archive_get_index();
    $dir = maca_menulist_attendance_archive_get_directory();
    $removed = 0;

    foreach ($index as $month_key => $entry) {
        $month = maca_menulist_attendance_archive_normalize_month($month_key);
        if ($month === '') {
            unset($index[$month_key]);
            continue;
        }

        $month_start = DateTimeImmutable::createFromFormat('Y-m-d', $month . '-01', $timezone);
        if (!$month_start || $month_start >= $cutoff) {
            continue;
        }

        if (is_array($entry)) {
            foreach (array('csv', 'html') as $key) {
                if (empty($entry[$key]) || !is_string($entry[$key])) {
                    continue;
                }

                $path = trailingslashit($dir) . basename($entry[$key]);
                if (file_exists($path)) {
                    wp_delete_file($path);
                }
            }
        }

        unset($index[$month]);
        $removed++;
    }

    if ($removed > 0) {
        maca_menulist_attendance_archive_save_index($index);
    }

    return $removed;
}

/**
 * Resolve a safe archive file path from index.
 *
 * @param string $year_month Month Y-m.
 * @param string $type       csv|html.
 * @return string Empty when not found.
 */
function maca_menulist_attendance_archive_get_file_path($year_month, $type) {
    $year_month = maca_menulist_attendance_archive_normalize_month($year_month);
    $type = $type === 'html' ? 'html' : 'csv';

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

    $index = maca_menulist_attendance_archive_get_index();
    if (empty($index[$year_month]) || !is_array($index[$year_month])) {
        return '';
    }

    $filename = $index[$year_month][$type] ?? '';
    if (!is_string($filename) || $filename === '') {
        return '';
    }

    $filename = basename($filename);
    $expected = maca_menulist_attendance_archive_base_filename($year_month) . '.' . $type;
    if ($filename !== $expected) {
        return '';
    }

    $path = trailingslashit(maca_menulist_attendance_archive_get_directory()) . $filename;

    return file_exists($path) ? $path : '';
}

/**
 * Cron callback — archive previous month.
 *
 * @return void
 */
function maca_menulist_attendance_archive_run_cron() {
    if (!maca_menulist_attendance_enabled() || !maca_menulist_attendance_archive_enabled()) {
        return;
    }

    $month = maca_menulist_attendance_archive_previous_month_key();
    $index = maca_menulist_attendance_archive_get_index();

    if (!isset($index[$month])) {
        maca_menulist_attendance_archive_create_month($month, 'cron');
    }

    maca_menulist_attendance_archive_schedule_cron();
}

/**
 * Next run: 1st of month at configured time (or next month if already passed).
 *
 * @return int
 */
function maca_menulist_attendance_archive_next_run_timestamp() {
    $time = maca_menulist_attendance_archive_get_run_time();
    $parts = explode(':', $time);
    $hour = isset($parts[0]) ? (int) $parts[0] : 3;
    $minute = isset($parts[1]) ? (int) $parts[1] : 0;

    $timezone = wp_timezone();
    $now = new DateTime('now', $timezone);
    $scheduled = DateTime::createFromFormat(
        'Y-m-d H:i:s',
        wp_date('Y-m-01', $now->getTimestamp()) . sprintf(' %02d:%02d:00', $hour, $minute),
        $timezone
    );

    if (!$scheduled instanceof DateTime) {
        return time() + DAY_IN_SECONDS;
    }

    if ($scheduled <= $now) {
        $scheduled->modify('+1 month');
    }

    return $scheduled->getTimestamp();
}

/**
 * Schedule monthly archive cron.
 *
 * @return void
 */
function maca_menulist_attendance_archive_schedule_cron() {
    maca_menulist_attendance_archive_unschedule_cron();

    if (!maca_menulist_attendance_enabled() || !maca_menulist_attendance_archive_enabled()) {
        return;
    }

    wp_schedule_single_event(
        maca_menulist_attendance_archive_next_run_timestamp(),
        MACA_MENULIST_ATTENDANCE_ARCHIVE_CRON_HOOK
    );
}

/**
 * Clear scheduled archive cron.
 *
 * @return void
 */
function maca_menulist_attendance_archive_unschedule_cron() {
    wp_clear_scheduled_hook(MACA_MENULIST_ATTENDANCE_ARCHIVE_CRON_HOOK);
}

/**
 * Ensure cron is scheduled when enabled.
 *
 * @return void
 */
function maca_menulist_attendance_archive_maybe_schedule_cron() {
    if (!maca_menulist_attendance_enabled() || !maca_menulist_attendance_archive_enabled()) {
        maca_menulist_attendance_archive_unschedule_cron();
        return;
    }

    if (!wp_next_scheduled(MACA_MENULIST_ATTENDANCE_ARCHIVE_CRON_HOOK)) {
        maca_menulist_attendance_archive_schedule_cron();
    }
}

/**
 * Save archive settings from admin.
 *
 * @param bool   $enabled          Enable automatic archive.
 * @param string $time             Run time HH:MM.
 * @param int    $retention_months Months to keep.
 * @return void
 */
function maca_menulist_attendance_archive_save_settings($enabled, $time, $retention_months) {
    update_option('maca_menulist_attendance_archive_enabled', $enabled ? '1' : '0');

    $time = is_string($time) ? trim($time) : '';
    if (preg_match('/^(\d{2}:\d{2})/', $time, $matches)) {
        update_option('maca_menulist_attendance_archive_time', $matches[1]);
    }

    update_option(
        'maca_menulist_attendance_archive_retention_months',
        max(12, min(120, (int) $retention_months))
    );

    maca_menulist_attendance_archive_schedule_cron();
}
