-- Migration 096: Reminder System for Sag (Cases)
-- Dato: 3. februar 2026
-- 
-- Features:
-- - Time-based reminders (specific datetime or cron-like scheduling)
-- - Status-change triggered reminders (via database trigger)
-- - Recurring reminders (once, daily, weekly, monthly)
-- - Multi-channel notifications (mattermost, email, frontend popup)
-- - User preferences with per-case overrides
-- - Global rate limiting (max 5 notifications per user per hour)

-- ============================================================================
-- User Notification Preferences Table
-- ============================================================================
CREATE TABLE IF NOT EXISTS user_notification_preferences (
    id SERIAL PRIMARY KEY,
    user_id INTEGER NOT NULL UNIQUE,
    
    -- Default notification channels (can be overridden per reminder)
    notify_mattermost BOOLEAN DEFAULT true,
    notify_email BOOLEAN DEFAULT false,
    notify_frontend BOOLEAN DEFAULT true,
    
    -- Email recipient (if different from user account)
    email_override VARCHAR(255),
    
    -- Quiet hours (no notifications outside these hours)
    quiet_hours_enabled BOOLEAN DEFAULT false,
    quiet_hours_start TIME,  -- e.g., 18:00
    quiet_hours_end TIME,    -- e.g., 08:00 (next day)
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP,
    
    CONSTRAINT email_format CHECK (email_override IS NULL OR email_override ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$')
);

-- ============================================================================
-- Reminder Rules Table
-- ============================================================================
CREATE TABLE IF NOT EXISTS sag_reminders (
    id SERIAL PRIMARY KEY,
    sag_id INTEGER NOT NULL REFERENCES sag_sager(id) ON DELETE CASCADE,
    
    -- Trigger Configuration
    trigger_type VARCHAR(50) NOT NULL CHECK (trigger_type IN 
        ('status_change', 'deadline_approaching', 'time_based')),
    trigger_config JSONB NOT NULL,
    -- Examples:
    -- status_change: {"target_status": "i_gang", "when_changed_to": "i_gang"}
    -- deadline_approaching: {"days_before": 3}
    -- time_based: {}
    
    -- Reminder Details
    title VARCHAR(255) NOT NULL,
    message TEXT,
    priority VARCHAR(20) DEFAULT 'normal' CHECK (priority IN 
        ('low', 'normal', 'high', 'urgent')),
    
    -- Notification Delivery
    notify_mattermost BOOLEAN,  -- NULL = use user default
    notify_email BOOLEAN,       -- NULL = use user default
    notify_frontend BOOLEAN,    -- NULL = use user default
    override_user_preferences BOOLEAN DEFAULT false,
    
    -- Recipient Configuration
    recipient_user_ids INTEGER[],  -- Array of user IDs to notify
    recipient_emails TEXT[],       -- Additional email addresses
    
    -- Recurrence Configuration
    recurrence_type VARCHAR(20) NOT NULL DEFAULT 'once' CHECK (recurrence_type IN 
        ('once', 'daily', 'weekly', 'monthly')),
    recurrence_day_of_week INTEGER,  -- 0-6 (0=Sunday) for weekly
    recurrence_day_of_month INTEGER, -- 1-31 for monthly
    
    -- Scheduling
    scheduled_at TIMESTAMP,         -- When reminder should first trigger
    next_check_at TIMESTAMP,        -- When to check/send next
    last_sent_at TIMESTAMP,
    
    -- State
    is_active BOOLEAN DEFAULT true,
    created_by_user_id INTEGER,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP,
    deleted_at TIMESTAMP,
    
    CONSTRAINT has_recipients CHECK (
        (recipient_user_ids IS NOT NULL AND array_length(recipient_user_ids, 1) > 0) OR
        (recipient_emails IS NOT NULL AND array_length(recipient_emails, 1) > 0)
    )
);

CREATE INDEX IF NOT EXISTS idx_sag_reminders_sag_id 
    ON sag_reminders(sag_id) WHERE is_active = true AND deleted_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_sag_reminders_next_check 
    ON sag_reminders(next_check_at) 
    WHERE is_active = true AND deleted_at IS NULL AND next_check_at IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_sag_reminders_active 
    ON sag_reminders(is_active, deleted_at) WHERE is_active = true AND deleted_at IS NULL;

-- ============================================================================
-- Reminder Queue Table (for trigger-based events)
-- ============================================================================
CREATE TABLE IF NOT EXISTS sag_reminder_queue (
    id SERIAL PRIMARY KEY,
    reminder_id INTEGER NOT NULL REFERENCES sag_reminders(id) ON DELETE CASCADE,
    sag_id INTEGER NOT NULL REFERENCES sag_sager(id) ON DELETE CASCADE,
    
    -- Event that triggered this
    trigger_event VARCHAR(50) NOT NULL,  -- e.g., 'status_changed'
    event_data JSONB,  -- e.g., {"old_status": "åben", "new_status": "i_gang"}
    
    -- Processing status
    status VARCHAR(20) DEFAULT 'pending' CHECK (status IN 
        ('pending', 'processing', 'sent', 'failed', 'rate_limited', 'skipped')),
    
    error_message TEXT,
    
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    processed_at TIMESTAMP,
    
    CONSTRAINT trigger_check CHECK (
        trigger_event IN ('status_changed', 'deadline_reached', 'manual')
    )
);

CREATE INDEX IF NOT EXISTS idx_sag_reminder_queue_status 
    ON sag_reminder_queue(status) WHERE status IN ('pending', 'processing');

CREATE INDEX IF NOT EXISTS idx_sag_reminder_queue_created 
    ON sag_reminder_queue(created_at) WHERE status = 'pending';

-- ============================================================================
-- Reminder Execution Log Table
-- ============================================================================
CREATE TABLE IF NOT EXISTS sag_reminder_logs (
    id SERIAL PRIMARY KEY,
    reminder_id INTEGER REFERENCES sag_reminders(id) ON DELETE SET NULL,
    sag_id INTEGER NOT NULL REFERENCES sag_sager(id) ON DELETE CASCADE,
    user_id INTEGER,  -- User who was notified (for rate limiting)
    
    -- Execution Details
    triggered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    channels_used TEXT[],  -- Array: ['mattermost', 'email', 'frontend']
    notification_payload JSONB,
    
    -- Delivery Status
    status VARCHAR(20) CHECK (status IN ('sent', 'failed', 'snoozed', 'dismissed', 'rate_limited')),
    error_message TEXT,
    
    -- User Actions
    acknowledged_by_user_id INTEGER,
    acknowledged_at TIMESTAMP,
    snoozed_until TIMESTAMP,
    snoozed_by_user_id INTEGER,
    dismissed_at TIMESTAMP,
    dismissed_by_user_id INTEGER,
    
    CONSTRAINT action_user_check CHECK (
        (acknowledged_by_user_id IS NULL AND acknowledged_at IS NULL) OR
        (acknowledged_by_user_id IS NOT NULL AND acknowledged_at IS NOT NULL)
    )
);

CREATE INDEX IF NOT EXISTS idx_sag_reminder_logs_user 
    ON sag_reminder_logs(user_id, triggered_at) 
    WHERE status = 'sent';

CREATE INDEX IF NOT EXISTS idx_sag_reminder_logs_sag 
    ON sag_reminder_logs(sag_id, triggered_at);

CREATE INDEX IF NOT EXISTS idx_sag_reminder_logs_status 
    ON sag_reminder_logs(status, snoozed_until)
    WHERE status IN ('snoozed', 'sent');

-- ============================================================================
-- Trigger Functions
-- ============================================================================

-- Function: Check rate limiting (global per user, max 5/hour)
CREATE OR REPLACE FUNCTION check_reminder_rate_limit(user_id_param INTEGER)
RETURNS BOOLEAN AS $$
DECLARE
    notification_count INTEGER;
BEGIN
    SELECT COUNT(*) INTO notification_count
    FROM sag_reminder_logs
    WHERE user_id = user_id_param
        AND triggered_at > CURRENT_TIMESTAMP - INTERVAL '1 hour'
        AND status = 'sent';
    
    RETURN notification_count < 5;
END;
$$ LANGUAGE plpgsql STABLE;

-- Trigger: Auto-update updated_at timestamp
CREATE OR REPLACE FUNCTION update_sag_reminders_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER sag_reminders_updated_at_trigger
BEFORE UPDATE ON sag_reminders
FOR EACH ROW
EXECUTE FUNCTION update_sag_reminders_updated_at();

-- Trigger: Auto-update user preferences updated_at
CREATE OR REPLACE FUNCTION update_user_notification_preferences_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER user_notification_preferences_updated_at_trigger
BEFORE UPDATE ON user_notification_preferences
FOR EACH ROW
EXECUTE FUNCTION update_user_notification_preferences_updated_at();

-- Trigger: Status change on sag_sager triggers reminders
CREATE OR REPLACE FUNCTION sag_status_change_reminder_trigger()
RETURNS TRIGGER AS $$
DECLARE
    reminder RECORD;
    recipient_user_id INTEGER;
    v_target_status VARCHAR;
BEGIN
    -- Only process if status actually changed
    IF OLD.status IS NOT DISTINCT FROM NEW.status THEN
        RETURN NEW;
    END IF;
    
    -- Find reminders with status_change trigger for this case
    FOR reminder IN
        SELECT *
        FROM sag_reminders
        WHERE sag_id = NEW.id
            AND is_active = true
            AND deleted_at IS NULL
            AND trigger_type = 'status_change'
    LOOP
        v_target_status := reminder.trigger_config->>'target_status';
        
        -- Queue event if reminder targets this new status
        IF v_target_status = NEW.status THEN
            INSERT INTO sag_reminder_queue (
                reminder_id,
                sag_id,
                trigger_event,
                event_data,
                status
            ) VALUES (
                reminder.id,
                NEW.id,
                'status_changed',
                jsonb_build_object(
                    'old_status', OLD.status,
                    'new_status', NEW.status,
                    'changed_at', CURRENT_TIMESTAMP
                ),
                'pending'
            );
        END IF;
    END LOOP;
    
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Create trigger on sag_sager
DROP TRIGGER IF EXISTS sag_status_change_reminder_trigger_exec ON sag_sager;
CREATE TRIGGER sag_status_change_reminder_trigger_exec
AFTER UPDATE OF status ON sag_sager
FOR EACH ROW
EXECUTE FUNCTION sag_status_change_reminder_trigger();

-- ============================================================================
-- Helper Views
-- ============================================================================

-- View: Pending reminders ready to send
CREATE OR REPLACE VIEW v_pending_reminders AS
SELECT 
    r.id,
    r.sag_id,
    r.title,
    r.message,
    r.priority,
    r.recipient_user_ids,
    r.recipient_emails,
    r.notify_mattermost,
    r.notify_email,
    r.notify_frontend,
    r.override_user_preferences,
    r.trigger_type,
    r.trigger_config,
    r.recurrence_type,
    r.scheduled_at,
    r.next_check_at
FROM sag_reminders r
WHERE r.is_active = true
    AND r.deleted_at IS NULL
    AND r.next_check_at IS NOT NULL
    AND r.next_check_at <= CURRENT_TIMESTAMP;

-- View: Pending queue events
CREATE OR REPLACE VIEW v_pending_reminder_queue AS
SELECT 
    q.id,
    q.reminder_id,
    q.sag_id,
    q.trigger_event,
    q.event_data,
    r.recipient_user_ids,
    r.recipient_emails,
    r.notify_mattermost,
    r.notify_email,
    r.notify_frontend,
    r.override_user_preferences,
    r.title,
    r.message,
    r.priority
FROM sag_reminder_queue q
JOIN sag_reminders r ON q.reminder_id = r.id
WHERE q.status = 'pending'
    AND r.is_active = true
    AND r.deleted_at IS NULL
ORDER BY r.priority DESC, q.created_at ASC;

-- ============================================================================
-- Comments
-- ============================================================================

COMMENT ON TABLE sag_reminders IS 'Defines reminder rules for cases (triggers, recipients, notifications)';
COMMENT ON TABLE sag_reminder_queue IS 'Queues reminder events from trigger functions for processing';
COMMENT ON TABLE sag_reminder_logs IS 'Logs all reminder notifications sent, including user interactions';
COMMENT ON TABLE user_notification_preferences IS 'User default notification preferences (can be overridden per reminder)';

COMMENT ON COLUMN sag_reminders.trigger_config IS 'JSON config for trigger type (e.g. {"target_status": "i_gang"})';
COMMENT ON COLUMN sag_reminders.recipient_user_ids IS 'PostgreSQL array of user IDs to notify';
COMMENT ON COLUMN sag_reminders.next_check_at IS 'When this reminder should be checked/sent next';
COMMENT ON COLUMN sag_reminder_logs.user_id IS 'User who received notification (for rate limiting)';
COMMENT ON FUNCTION check_reminder_rate_limit IS 'Returns true if user has sent <5 notifications in past hour';
