-- KB spine: mail_sync_state — per-folder IMAP sync cursor for jobs/mail-imap-sync
-- Additive: does not modify 001_envelope.sql .. 004_summaries.sql.
-- Version: 005 — mail_sync_state (faza mailowa Krok 7, recon kb/audits/mail-sync-2026-08-06.md
--                §2.2 wariant A + Decyzja (f), zatwierdzona przez operatora 2026-08-06)
--
-- Why a table and not a file in /opt/homelab/state/: the sync cursor and the envelopes it
-- describes must restore together or not at all. A state file surviving a DB restore makes the
-- poller silently skip everything between the restored envelope set and the file's last_uid —
-- a failure with no symptom, noticed months later as missing mail. Backing it into the same
-- database buys that invariant for the price of this one migration.
--
-- Keyed (account, folder) because the folder scope is per account (Decyzja (e)): gmail syncs a
-- single SPECIAL-USE \All folder, fastmail syncs INBOX + Archive + Sent. Adding a folder is
-- then a config change, not a migration — Message-ID dedup absorbs any overlap.
--
-- account matches envelope.source ('gmail' | 'fastmail'); folder is the IMAP mailbox name
-- exactly as the server returned it in LIST (never a hardcoded literal — Gmail localizes
-- '[Gmail]/All Mail', so the name is resolved by SPECIAL-USE attribute at runtime).

CREATE TABLE IF NOT EXISTS mail_sync_state (
    account      TEXT        NOT NULL,           -- == envelope.source: gmail | fastmail
    folder       TEXT        NOT NULL,           -- IMAP mailbox name as returned by LIST
    uidvalidity  BIGINT      NOT NULL,           -- server's UIDVALIDITY; a change invalidates last_uid
    last_uid     BIGINT      NOT NULL,           -- highest UID processed to completion (archive + envelope durable)
    last_sync_ts TIMESTAMPTZ NOT NULL DEFAULT now(),
    PRIMARY KEY (account, folder)
);
