-- SSL renewal resilience: per-domain state machine, retry tracking, dedup table.
-- Idempotent — IF NOT EXISTS where supported, manual checks otherwise.

USE whp;

-- 1. Add per-domain renewal state columns to haproxy_domains
--    Use prepared-statement guarded ALTERs so the migration is safe to re-run.
DROP PROCEDURE IF EXISTS whp_add_renewal_state_cols;
DELIMITER $$
CREATE PROCEDURE whp_add_renewal_state_cols()
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'whp' AND table_name = 'haproxy_domains'
          AND column_name = 'renewal_state'
    ) THEN
        ALTER TABLE whp.haproxy_domains
            ADD COLUMN renewal_state ENUM(
                'ok',
                'pending',
                'transient_failure',
                'unreachable',
                'manual_review',
                'deferred'
            ) NOT NULL DEFAULT 'pending'
            COMMENT 'State machine for renewal orchestrator. ok=current; pending=never-issued or freshly-scheduled; transient_failure=will retry with backoff; unreachable=domain DNS does not resolve to us; manual_review=retries exhausted, customer action required; deferred=excluded from auto-renewal (e.g. customer offboarded)';
    END IF;

    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'whp' AND table_name = 'haproxy_domains'
          AND column_name = 'last_renewal_error'
    ) THEN
        ALTER TABLE whp.haproxy_domains
            ADD COLUMN last_renewal_error TEXT NULL
            COMMENT 'Most recent failure reason; empty when last attempt succeeded';
    END IF;

    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'whp' AND table_name = 'haproxy_domains'
          AND column_name = 'last_renewal_attempt_at'
    ) THEN
        ALTER TABLE whp.haproxy_domains
            ADD COLUMN last_renewal_attempt_at TIMESTAMP NULL DEFAULT NULL
            COMMENT 'When the orchestrator last tried to renew/issue this cert (success or failure)';
    END IF;

    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'whp' AND table_name = 'haproxy_domains'
          AND column_name = 'next_renewal_attempt_at'
    ) THEN
        ALTER TABLE whp.haproxy_domains
            ADD COLUMN next_renewal_attempt_at TIMESTAMP NULL DEFAULT NULL
            COMMENT 'Earliest time the orchestrator should retry this domain. NULL = pick on next sweep';
    END IF;

    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'whp' AND table_name = 'haproxy_domains'
          AND column_name = 'consecutive_failures'
    ) THEN
        ALTER TABLE whp.haproxy_domains
            ADD COLUMN consecutive_failures INT NOT NULL DEFAULT 0
            COMMENT 'Resets to 0 on successful renewal; drives exponential backoff';
    END IF;

    IF NOT EXISTS (
        SELECT 1 FROM information_schema.columns
        WHERE table_schema = 'whp' AND table_name = 'haproxy_domains'
          AND column_name = 'dns_validation'
    ) THEN
        ALTER TABLE whp.haproxy_domains
            ADD COLUMN dns_validation TINYINT(1) NOT NULL DEFAULT 0
            COMMENT 'Whether DNS-01 validation should be used. Forced 1 for is_wildcard=1';
    END IF;
END$$
DELIMITER ;
CALL whp_add_renewal_state_cols();
DROP PROCEDURE IF EXISTS whp_add_renewal_state_cols;

-- 2. Index for the cron sweep query (renewal_state, next_renewal_attempt_at)
DROP PROCEDURE IF EXISTS whp_add_renewal_sweep_idx;
DELIMITER $$
CREATE PROCEDURE whp_add_renewal_sweep_idx()
BEGIN
    IF NOT EXISTS (
        SELECT 1 FROM information_schema.statistics
        WHERE table_schema = 'whp' AND table_name = 'haproxy_domains'
          AND index_name = 'idx_renewal_sweep'
    ) THEN
        ALTER TABLE whp.haproxy_domains
            ADD INDEX idx_renewal_sweep (renewal_state, next_renewal_attempt_at);
    END IF;
END$$
DELIMITER ;
CALL whp_add_renewal_sweep_idx();
DROP PROCEDURE IF EXISTS whp_add_renewal_sweep_idx;

-- 3. Backfill renewal_state for existing rows that have ssl_enabled=1
--    so the orchestrator's first sweep treats them as healthy until proven otherwise.
--    The backfill *script* (not this SQL) does the deeper audit against /etc/letsencrypt/live/.
UPDATE whp.haproxy_domains
SET renewal_state = 'ok'
WHERE ssl_enabled = 1
  AND renewal_state = 'pending'
  AND ssl_last_renewal IS NOT NULL
  AND ssl_last_renewal > (NOW() - INTERVAL 60 DAY);

-- 4. Notification log table — drives 7-day per-domain cooldown and digest grouping.
CREATE TABLE IF NOT EXISTS whp.ssl_notification_log (
    id INT NOT NULL AUTO_INCREMENT,
    domain_name VARCHAR(255) NOT NULL,
    site_id INT NULL COMMENT 'Owning site_id at time of send; NULL if domain not bound to a site',
    customer_username VARCHAR(64) NULL COMMENT 'Owner username at time of send',
    notification_type ENUM(
        'unreachable',
        'manual_review',
        'recovered',
        'dns_validation_required'
    ) NOT NULL,
    recipient_email VARCHAR(255) NOT NULL,
    sent_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id),
    INDEX idx_dedup (domain_name, notification_type, sent_at),
    INDEX idx_customer_recent (customer_username, sent_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Per-(domain,type) email send history. Orchestrator queries this for 7d cooldown + per-customer digest grouping.';

-- 5. Settings live in /docker/whp/settings.json (not a DB table) — see
--    scripts/updates/staging/seed-ssl-renewal-settings.sh which adds the
--    default ssl_renewal block to settings.json idempotently.
