*/ public static function statements(string $driver): array { return $driver === 'mysql' ? self::mysql() : self::sqlite(); } /** @return list */ private static function sqlite(): array { return [ 'CREATE TABLE IF NOT EXISTS settings ( setting_key TEXT PRIMARY KEY, setting_value TEXT NOT NULL, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )', 'CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, totp_secret TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )', 'CREATE TABLE IF NOT EXISTS monthly_plans ( id INTEGER PRIMARY KEY AUTOINCREMENT, month TEXT NOT NULL UNIQUE, starting_income NUMERIC NOT NULL DEFAULT 0, rollover_in NUMERIC NOT NULL DEFAULT 0, rolling_enabled INTEGER NOT NULL DEFAULT 1, rollover_source_month TEXT, notes TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )', 'CREATE TABLE IF NOT EXISTS income_entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, month TEXT NOT NULL, label TEXT NOT NULL, amount NUMERIC NOT NULL, received_on TEXT NOT NULL, withholding_amount NUMERIC NOT NULL DEFAULT 0, notes TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )', 'CREATE INDEX IF NOT EXISTS idx_income_month ON income_entries(month)', 'CREATE TABLE IF NOT EXISTS categories ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, color TEXT NOT NULL DEFAULT "#7c5cff", monthly_limit NUMERIC NOT NULL DEFAULT 0, active INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )', 'CREATE TABLE IF NOT EXISTS budget_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, month TEXT NOT NULL, category_id INTEGER, name TEXT NOT NULL, planned_amount NUMERIC NOT NULL DEFAULT 0, due_date TEXT, recurrence TEXT NOT NULL DEFAULT "none", series_key TEXT, status TEXT NOT NULL DEFAULT "active", notes TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL )', 'CREATE INDEX IF NOT EXISTS idx_budget_items_month ON budget_items(month)', 'CREATE UNIQUE INDEX IF NOT EXISTS idx_budget_items_series_month ON budget_items(series_key, month) WHERE series_key IS NOT NULL', 'CREATE TABLE IF NOT EXISTS transactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, month TEXT NOT NULL, budget_item_id INTEGER, category_id INTEGER, merchant TEXT NOT NULL, amount NUMERIC NOT NULL, transacted_on TEXT NOT NULL, dead_pull_amount NUMERIC NOT NULL DEFAULT 0, notes TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (budget_item_id) REFERENCES budget_items(id) ON DELETE SET NULL, FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL )', 'CREATE INDEX IF NOT EXISTS idx_transactions_month ON transactions(month)', 'CREATE TABLE IF NOT EXISTS dead_account_entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, month TEXT NOT NULL, kind TEXT NOT NULL, amount NUMERIC NOT NULL, source_type TEXT, source_id INTEGER, notes TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )', 'CREATE INDEX IF NOT EXISTS idx_dead_month ON dead_account_entries(month)', 'CREATE INDEX IF NOT EXISTS idx_dead_source ON dead_account_entries(source_type, source_id)', 'CREATE TABLE IF NOT EXISTS calendar_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, month TEXT NOT NULL, title TEXT NOT NULL, event_date TEXT NOT NULL, event_type TEXT NOT NULL DEFAULT "note", notes TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )', 'CREATE TABLE IF NOT EXISTS alert_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, budget_item_id INTEGER NOT NULL, alert_date TEXT NOT NULL, window_days INTEGER NOT NULL, recipient TEXT NOT NULL, status TEXT NOT NULL, response TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE (budget_item_id, alert_date, window_days, recipient), FOREIGN KEY (budget_item_id) REFERENCES budget_items(id) ON DELETE CASCADE )', 'CREATE TABLE IF NOT EXISTS visits ( id INTEGER PRIMARY KEY AUTOINCREMENT, route TEXT NOT NULL, visitor_hash TEXT NOT NULL, visited_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP )', 'CREATE INDEX IF NOT EXISTS idx_visits_date ON visits(visited_at)', 'CREATE TABLE IF NOT EXISTS audit_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, action TEXT NOT NULL, entity_type TEXT, entity_id INTEGER, metadata TEXT, created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL )', ]; } /** @return list */ private static function mysql(): array { return [ 'CREATE TABLE IF NOT EXISTS settings ( setting_key VARCHAR(191) PRIMARY KEY, setting_value LONGTEXT NOT NULL, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, totp_secret VARCHAR(64) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS monthly_plans ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, month CHAR(7) NOT NULL UNIQUE, starting_income DECIMAL(14,2) NOT NULL DEFAULT 0, rollover_in DECIMAL(14,2) NOT NULL DEFAULT 0, rolling_enabled TINYINT(1) NOT NULL DEFAULT 1, rollover_source_month CHAR(7) NULL, notes TEXT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS income_entries ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, month CHAR(7) NOT NULL, label VARCHAR(255) NOT NULL, amount DECIMAL(14,2) NOT NULL, received_on DATE NOT NULL, withholding_amount DECIMAL(14,2) NOT NULL DEFAULT 0, notes TEXT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_income_month (month) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS categories ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(191) NOT NULL UNIQUE, color VARCHAR(16) NOT NULL DEFAULT "#7c5cff", monthly_limit DECIMAL(14,2) NOT NULL DEFAULT 0, active TINYINT(1) NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS budget_items ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, month CHAR(7) NOT NULL, category_id BIGINT UNSIGNED NULL, name VARCHAR(255) NOT NULL, planned_amount DECIMAL(14,2) NOT NULL DEFAULT 0, due_date DATE NULL, recurrence VARCHAR(16) NOT NULL DEFAULT "none", series_key VARCHAR(64) NULL, status VARCHAR(24) NOT NULL DEFAULT "active", notes TEXT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_budget_items_month (month), UNIQUE KEY idx_budget_items_series_month (series_key, month), CONSTRAINT fk_item_category FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS transactions ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, month CHAR(7) NOT NULL, budget_item_id BIGINT UNSIGNED NULL, category_id BIGINT UNSIGNED NULL, merchant VARCHAR(255) NOT NULL, amount DECIMAL(14,2) NOT NULL, transacted_on DATE NOT NULL, dead_pull_amount DECIMAL(14,2) NOT NULL DEFAULT 0, notes TEXT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_transactions_month (month), CONSTRAINT fk_transaction_item FOREIGN KEY (budget_item_id) REFERENCES budget_items(id) ON DELETE SET NULL, CONSTRAINT fk_transaction_category FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS dead_account_entries ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, month CHAR(7) NOT NULL, kind VARCHAR(32) NOT NULL, amount DECIMAL(14,2) NOT NULL, source_type VARCHAR(32) NULL, source_id BIGINT NULL, notes TEXT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_dead_month (month), INDEX idx_dead_source (source_type, source_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS calendar_events ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, month CHAR(7) NOT NULL, title VARCHAR(255) NOT NULL, event_date DATE NOT NULL, event_type VARCHAR(24) NOT NULL DEFAULT "note", notes TEXT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS alert_logs ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, budget_item_id BIGINT UNSIGNED NOT NULL, alert_date DATE NOT NULL, window_days INT NOT NULL, recipient VARCHAR(255) NOT NULL, status VARCHAR(24) NOT NULL, response TEXT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY alert_unique (budget_item_id, alert_date, window_days, recipient), CONSTRAINT fk_alert_item FOREIGN KEY (budget_item_id) REFERENCES budget_items(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS visits ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, route VARCHAR(191) NOT NULL, visitor_hash VARCHAR(64) NOT NULL, visited_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_visits_date (visited_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', 'CREATE TABLE IF NOT EXISTS audit_logs ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NULL, action VARCHAR(191) NOT NULL, entity_type VARCHAR(64) NULL, entity_id BIGINT NULL, metadata LONGTEXT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_audit_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4', ]; } }