<?php

namespace Formatrica\Submission;

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

use Formatrica\Forms\Form_Repository;
use Formatrica\Submission\Submission_Repository;

final class Submission_Cleanup
{
    private const CRON_HOOK = 'formatrica_cleanup_submissions';

    /** Rows retired per DELETE, so a huge backlog is chunked, not one big lock. */
    private const BATCH_SIZE = 500;

    /** Hard cap on rows deleted per cron run (backstop against runaway sweeps). */
    private const MAX_DELETIONS = 50000;

    /** Wall-clock budget per run; the sweep stops cleanly when exceeded. */
    private const TIME_BUDGET_SECONDS = 25;

    /** Grace window before a deleted form's orphaned submissions are purged. */
    private const ORPHAN_GRACE_DAYS = 30;

    private Form_Repository $forms;
    private Submission_Repository $submissions;

    public function __construct()
    {
        $this->forms = new Form_Repository();
        $this->submissions = new Submission_Repository();
    }

    public function boot(): void
    {
        add_action(self::CRON_HOOK, [$this, 'cleanup_old_submissions']);

        // Schedule the cron job if not already scheduled
        if (!wp_next_scheduled(self::CRON_HOOK)) {
            wp_schedule_event(time(), 'daily', self::CRON_HOOK);
        }
    }

    /**
     * Enforce per-form submission retention, plus an orphan sweep.
     *
     * Runs daily via cron. Bounded by a batch size, a global max-deletions
     * budget, and a wall-clock deadline so a large backlog is retired safely
     * over several runs rather than in one unbounded, table-locking DELETE.
     * Each form is isolated (one form's error is logged and skipped, not fatal).
     * Applies to active AND inactive forms — a deactivated form's retention
     * promise still holds. An orphan sweep then purges submissions whose parent
     * form was hard-deleted, after a grace window, so deleted-form PII does not
     * linger.
     */
    public function cleanup_old_submissions(): void
    {
        $started_at = time();
        $deleted_total = 0;

        // Per-form retention across all forms (active + inactive).
        foreach ($this->forms->all('all') as $form) {
            if ($this->budget_exhausted($started_at, $deleted_total)) {
                break;
            }

            try {
                $form_id = (int) ($form['id'] ?? 0);
                $options = $form['config']['options'] ?? [];
                $enabled = !empty($options['delete_submissions_after_days_enabled']);
                $days = (int) ($options['delete_submissions_after_days'] ?? 90);

                if (!$enabled || $days < 1 || $form_id <= 0) {
                    continue;
                }

                $cutoff = gmdate('Y-m-d H:i:s', $started_at - $days * DAY_IN_SECONDS);

                do {
                    $limit = min(self::BATCH_SIZE, self::MAX_DELETIONS - $deleted_total);
                    if ($limit <= 0) {
                        break;
                    }
                    $deleted = $this->submissions->delete_older_than($form_id, $cutoff, $limit);
                    $deleted_total += $deleted;
                } while ($deleted === $limit && !$this->budget_exhausted($started_at, $deleted_total));
            } catch (\Throwable $e) {
                // Failure isolation: one form's error must not abort the run.
                do_action('formatrica_debug_log', 'Retention sweep error: ' . $e->getMessage());
                continue;
            }
        }

        // Orphan sweep: submissions of hard-deleted forms, after a grace window.
        if (!$this->budget_exhausted($started_at, $deleted_total)) {
            $orphan_cutoff = gmdate('Y-m-d H:i:s', $started_at - self::ORPHAN_GRACE_DAYS * DAY_IN_SECONDS);

            do {
                $limit = min(self::BATCH_SIZE, self::MAX_DELETIONS - $deleted_total);
                if ($limit <= 0) {
                    break;
                }
                $deleted = $this->submissions->delete_orphans_older_than($orphan_cutoff, $limit);
                $deleted_total += $deleted;
            } while ($deleted === $limit && !$this->budget_exhausted($started_at, $deleted_total));
        }

        if ($deleted_total > 0) {
            do_action(
                'formatrica_debug_log',
                sprintf('Retention sweep deleted %d submissions this run.', $deleted_total)
            );
        }
    }

    /**
     * Whether the run has hit its deletion budget or wall-clock deadline.
     */
    private function budget_exhausted(int $started_at, int $deleted_total): bool
    {
        return $deleted_total >= self::MAX_DELETIONS
            || (time() - $started_at) >= self::TIME_BUDGET_SECONDS;
    }

    /**
     * Unschedule the cron job (called on deactivation)
     */
    public static function unschedule(): void
    {
        $timestamp = wp_next_scheduled(self::CRON_HOOK);
        if ($timestamp) {
            wp_unschedule_event($timestamp, self::CRON_HOOK);
        }
    }
}
