<?php

namespace WHPBackup;

require_once('/docker/whp/web/libs/mysqlmgmt.php');
require_once('/docker/whp/web/libs/BackupTarget.php');
require_once('/docker/whp/web/libs/BackupSchedule.php');

use PDO;
use Exception;

/**
 * Creates a default backup schedule for a freshly-provisioned (or existing,
 * un-scheduled) WHP user. Inserts one row per asset type — site, userfiles,
 * database (MySQL), postgres_database — each with resource_name = NULL so the
 * scheduler fans them out across the user's domains/databases at run time.
 *
 * Times are staggered deterministically by crc32(username) so the same user
 * lands in the same window on re-runs (idempotent), and load is spread evenly
 * across users instead of pinning all four asset types of every user to the
 * same minute.
 */
class BackupDefaults {

    /**
     * Idempotent. Returns ['created' => N, 'skipped' => bool, 'reason' => string|null].
     * Never throws on user-data issues; only on programmer errors. Callers in
     * the user-creation hook wrap this in try/catch so backup-schedule failure
     * never rolls back account creation.
     */
    public static function createForUser(PDO $db, string $username): array {
        if (empty($username)) {
            return ['created' => 0, 'skipped' => true, 'reason' => 'empty username'];
        }

        // Idempotency: if any schedule exists for this user, leave it alone.
        // Backfill might run multiple times; new-user creation might be retried;
        // either way we don't pile on duplicate schedules.
        $exists = $db->prepare("SELECT 1 FROM backup_schedules WHERE user = ? LIMIT 1");
        $exists->execute([$username]);
        if ($exists->fetch(PDO::FETCH_ASSOC)) {
            return ['created' => 0, 'skipped' => true, 'reason' => 'schedule already exists'];
        }

        // Find the default target. getDefault() returns a global target if one
        // is configured, else the user's own, else null. We don't synthesize a
        // target here — that would require asking the admin where to send data.
        $target = BackupTarget::getDefault($db, $username);
        if ($target === null) {
            error_log("BackupDefaults: no default backup target available for {$username}; skipping default schedule creation");
            return ['created' => 0, 'skipped' => true, 'reason' => 'no default target'];
        }
        $targetId = $target->getId();

        // Stagger schedule times within the 01:00–04:59 window. crc32 is
        // deterministic — same input always yields the same time, which keeps
        // the backfill script idempotent and avoids concentrating load.
        list($siteTime, $userfilesTime, $dbTime, $pgTime) = self::staggeredTimes($username);

        $rows = [
            ['site',              $siteTime],
            ['userfiles',         $userfilesTime],
            ['database',          $dbTime],
            ['postgres_database', $pgTime],
        ];

        $created = 0;
        foreach ($rows as $row) {
            list($type, $time) = $row;
            try {
                $schedule = new BackupSchedule($db);
                $ok = $schedule->create([
                    'user'          => $username,
                    'target_id'     => $targetId,
                    'backup_type'   => $type,
                    'resource_name' => null,
                    'schedule_type' => 'daily',
                    'schedule_time' => $time,
                    'schedule_day'  => null,
                    'max_retention' => 30,
                    'is_active'     => 1,
                ]);
                if ($ok) {
                    $created++;
                }
            } catch (Exception $e) {
                error_log("BackupDefaults: failed to create {$type} schedule for {$username}: " . $e->getMessage());
            }
        }

        return ['created' => $created, 'skipped' => false, 'reason' => null];
    }

    /**
     * Returns [site, userfiles, db, pg] times in 'HH:MM:00' format. Window is
     * 01:00–04:59 with each asset type one hour later than the previous.
     */
    private static function staggeredTimes(string $username): array {
        $h = crc32($username);
        $hour   = ($h % 4) + 1;       // 1..4
        $minute = (($h >> 8) % 60);   // 0..59
        $fmt = function ($hr, $mn) {
            return sprintf('%02d:%02d:00', $hr, $mn);
        };
        return [
            $fmt($hour,     $minute),
            $fmt($hour + 1, $minute),
            $fmt($hour + 2, $minute),
            $fmt($hour + 3, $minute),
        ];
    }
}
