-- ============================================================================
-- Mitai Jinkendo v9c: Subscription & Coupon System Migration
-- ============================================================================
-- Created: 2026-03-19
-- Purpose: Add flexible tier system with Feature-Registry Pattern
--
-- Tables added:
--   1. app_settings          - Global configuration
--   2. tiers                 - Subscription tiers (simplified)
--   3. features              - Feature registry (all limitable features)
--   4. tier_limits           - Tier x Feature matrix
--   5. user_feature_restrictions - Individual user overrides
--   6. user_feature_usage    - Usage tracking
--   7. coupons               - Coupon management
--   8. coupon_redemptions    - Redemption history
--   9. access_grants         - Time-limited access grants
--  10. user_activity_log     - Activity tracking
--  11. user_stats            - Aggregated statistics
--
-- Feature-Registry Pattern:
--   Instead of hardcoded columns (max_weight_entries, max_ai_calls),
--   all limits are defined in features table and configured via tier_limits.
--   This allows adding new limitable features without schema changes.
-- ============================================================================

-- ============================================================================
-- 1. app_settings - Global configuration
-- ============================================================================
CREATE TABLE IF NOT EXISTS app_settings (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL,
    description TEXT,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ============================================================================
-- 2. tiers - Subscription tiers (simplified)
-- ============================================================================
CREATE TABLE IF NOT EXISTS tiers (
    id TEXT PRIMARY KEY,              -- 'free', 'basic', 'premium', 'selfhosted'
    name TEXT NOT NULL,               -- Display name
    description TEXT,                 -- Marketing description
    price_monthly_cents INTEGER,      -- NULL for free/selfhosted
    price_yearly_cents INTEGER,       -- NULL for free/selfhosted
    stripe_price_id_monthly TEXT,     -- Stripe Price ID (for v9d)
    stripe_price_id_yearly TEXT,      -- Stripe Price ID (for v9d)
    active BOOLEAN DEFAULT true,      -- Can new users subscribe?
    sort_order INTEGER DEFAULT 0,
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ============================================================================
-- 3. features - Feature registry (all limitable features)
-- ============================================================================
CREATE TABLE IF NOT EXISTS features (
    id TEXT PRIMARY KEY,              -- 'weight_entries', 'ai_calls', 'photos', etc.
    name TEXT NOT NULL,               -- Display name
    description TEXT,                 -- What is this feature?
    category TEXT,                    -- 'data', 'ai', 'export', 'integration'
    limit_type TEXT DEFAULT 'count',  -- 'count', 'boolean', 'quota'
    reset_period TEXT DEFAULT 'never', -- 'never', 'monthly', 'daily'
    default_limit INTEGER,            -- Fallback if no tier_limit defined
    active BOOLEAN DEFAULT true,      -- Is this feature currently used?
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ============================================================================
-- 4. tier_limits - Tier x Feature matrix
-- ============================================================================
CREATE TABLE IF NOT EXISTS tier_limits (
    id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::TEXT,
    tier_id TEXT NOT NULL REFERENCES tiers(id) ON DELETE CASCADE,
    feature_id TEXT NOT NULL REFERENCES features(id) ON DELETE CASCADE,
    limit_value INTEGER,              -- NULL = unlimited, 0 = disabled
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(tier_id, feature_id)
);

-- ============================================================================
-- 5. user_feature_restrictions - Individual user overrides
-- ============================================================================
CREATE TABLE IF NOT EXISTS user_feature_restrictions (
    id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::TEXT,
    profile_id TEXT NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    feature_id TEXT NOT NULL REFERENCES features(id) ON DELETE CASCADE,
    limit_value INTEGER,              -- NULL = unlimited, 0 = disabled
    reason TEXT,                      -- Why was this override applied?
    created_by TEXT,                  -- Admin profile_id
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(profile_id, feature_id)
);

-- ============================================================================
-- 6. user_feature_usage - Usage tracking
-- ============================================================================
CREATE TABLE IF NOT EXISTS user_feature_usage (
    id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::TEXT,
    profile_id TEXT NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    feature_id TEXT NOT NULL REFERENCES features(id) ON DELETE CASCADE,
    usage_count INTEGER DEFAULT 0,
    reset_at TIMESTAMP,               -- When does this counter reset?
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(profile_id, feature_id)
);

-- ============================================================================
-- 7. coupons - Coupon management
-- ============================================================================
CREATE TABLE IF NOT EXISTS coupons (
    id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::TEXT,
    code TEXT UNIQUE NOT NULL,
    type TEXT NOT NULL,               -- 'single_use', 'period', 'wellpass'
    tier_id TEXT REFERENCES tiers(id) ON DELETE SET NULL,
    duration_days INTEGER,            -- For period/wellpass coupons
    max_redemptions INTEGER,          -- NULL = unlimited
    redemption_count INTEGER DEFAULT 0,
    valid_from TIMESTAMP,
    valid_until TIMESTAMP,
    active BOOLEAN DEFAULT true,
    created_by TEXT,                  -- Admin profile_id
    description TEXT,                 -- Internal note
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ============================================================================
-- 8. coupon_redemptions - Redemption history
-- ============================================================================
CREATE TABLE IF NOT EXISTS coupon_redemptions (
    id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::TEXT,
    coupon_id TEXT NOT NULL REFERENCES coupons(id) ON DELETE CASCADE,
    profile_id TEXT NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    redeemed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    access_grant_id TEXT,             -- FK to access_grants (created as result)
    UNIQUE(coupon_id, profile_id)     -- One redemption per user per coupon
);

-- ============================================================================
-- 9. access_grants - Time-limited access grants
-- ============================================================================
CREATE TABLE IF NOT EXISTS access_grants (
    id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::TEXT,
    profile_id TEXT NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    tier_id TEXT NOT NULL REFERENCES tiers(id) ON DELETE CASCADE,
    granted_by TEXT,                  -- 'coupon', 'admin', 'trial', 'subscription'
    coupon_id TEXT REFERENCES coupons(id) ON DELETE SET NULL,
    valid_from TIMESTAMP NOT NULL,
    valid_until TIMESTAMP NOT NULL,
    is_active BOOLEAN DEFAULT true,   -- Can be paused by Wellpass logic
    paused_by TEXT,                   -- access_grant.id that paused this
    paused_at TIMESTAMP,              -- When was it paused?
    remaining_days INTEGER,           -- Days left when paused (for resume)
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ============================================================================
-- 10. user_activity_log - Activity tracking
-- ============================================================================
CREATE TABLE IF NOT EXISTS user_activity_log (
    id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::TEXT,
    profile_id TEXT NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    action TEXT NOT NULL,             -- 'login', 'logout', 'coupon_redeemed', 'tier_changed'
    details JSONB,                    -- Flexible metadata
    ip_address TEXT,
    user_agent TEXT,
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_activity_log_profile ON user_activity_log(profile_id, created DESC);
CREATE INDEX IF NOT EXISTS idx_activity_log_action ON user_activity_log(action, created DESC);

-- ============================================================================
-- 11. user_stats - Aggregated statistics
-- ============================================================================
CREATE TABLE IF NOT EXISTS user_stats (
    profile_id TEXT PRIMARY KEY REFERENCES profiles(id) ON DELETE CASCADE,
    last_login TIMESTAMP,
    login_count INTEGER DEFAULT 0,
    weight_entries_count INTEGER DEFAULT 0,
    ai_calls_count INTEGER DEFAULT 0,
    photos_count INTEGER DEFAULT 0,
    total_data_points INTEGER DEFAULT 0,
    created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ============================================================================
-- Extend profiles table with subscription fields
-- ============================================================================
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS tier TEXT DEFAULT 'free';
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS trial_ends_at TIMESTAMP;
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS email_verified BOOLEAN DEFAULT false;
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS email_verify_token TEXT;
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS invited_by TEXT REFERENCES profiles(id) ON DELETE SET NULL;
ALTER TABLE profiles ADD COLUMN IF NOT EXISTS invitation_token TEXT;

-- ============================================================================
-- Insert initial data
-- ============================================================================

-- App settings
INSERT INTO app_settings (key, value, description) VALUES
    ('trial_duration_days', '14', 'Default trial duration for new registrations'),
    ('post_trial_tier', 'free', 'Tier after trial expires (free/disabled)'),
    ('require_email_verification', 'true', 'Require email verification before activation'),
    ('self_registration_enabled', 'true', 'Allow self-registration')
ON CONFLICT (key) DO NOTHING;

-- Tiers
INSERT INTO tiers (id, name, description, price_monthly_cents, price_yearly_cents, active, sort_order) VALUES
    ('free', 'Free', 'Eingeschränkte Basis-Funktionen', NULL, NULL, true, 1),
    ('basic', 'Basic', 'Kernfunktionen ohne KI', 499, 4990, true, 2),
    ('premium', 'Premium', 'Alle Features inkl. KI und Connectoren', 999, 9990, true, 3),
    ('selfhosted', 'Self-Hosted', 'Unbegrenzt (für Heimserver)', NULL, NULL, false, 4)
ON CONFLICT (id) DO NOTHING;

-- Features (11 initial features)
INSERT INTO features (id, name, description, category, limit_type, reset_period, default_limit, active) VALUES
    ('weight_entries', 'Gewichtseinträge', 'Anzahl Gewichtsmessungen', 'data', 'count', 'never', NULL, true),
    ('circumference_entries', 'Umfangs-Einträge', 'Anzahl Umfangsmessungen', 'data', 'count', 'never', NULL, true),
    ('caliper_entries', 'Caliper-Einträge', 'Anzahl Hautfaltenmessungen', 'data', 'count', 'never', NULL, true),
    ('nutrition_entries', 'Ernährungs-Einträge', 'Anzahl Ernährungslogs', 'data', 'count', 'never', NULL, true),
    ('activity_entries', 'Aktivitäts-Einträge', 'Anzahl Trainings/Aktivitäten', 'data', 'count', 'never', NULL, true),
    ('photos', 'Progress-Fotos', 'Anzahl hochgeladene Fotos', 'data', 'count', 'never', NULL, true),
    ('ai_calls', 'KI-Analysen', 'KI-Auswertungen pro Monat', 'ai', 'count', 'monthly', 0, true),
    ('ai_pipeline', 'KI-Pipeline', 'Vollständige Pipeline-Analyse', 'ai', 'boolean', 'never', 0, true),
    ('export_csv', 'CSV-Export', 'Daten als CSV exportieren', 'export', 'boolean', 'never', 0, true),
    ('export_json', 'JSON-Export', 'Daten als JSON exportieren', 'export', 'boolean', 'never', 0, true),
    ('export_zip', 'ZIP-Export', 'Vollständiger Backup-Export', 'export', 'boolean', 'never', 0, true)
ON CONFLICT (id) DO NOTHING;

-- Tier x Feature Matrix (tier_limits)
-- Format: (tier, feature, limit) - NULL = unlimited, 0 = disabled

-- FREE tier (sehr eingeschränkt)
INSERT INTO tier_limits (tier_id, feature_id, limit_value) VALUES
    ('free', 'weight_entries', 30),
    ('free', 'circumference_entries', 10),
    ('free', 'caliper_entries', 10),
    ('free', 'nutrition_entries', 30),
    ('free', 'activity_entries', 30),
    ('free', 'photos', 5),
    ('free', 'ai_calls', 0),           -- Keine KI
    ('free', 'ai_pipeline', 0),        -- Keine Pipeline
    ('free', 'export_csv', 0),         -- Kein Export
    ('free', 'export_json', 0),
    ('free', 'export_zip', 0)
ON CONFLICT (tier_id, feature_id) DO NOTHING;

-- BASIC tier (Kernfunktionen)
INSERT INTO tier_limits (tier_id, feature_id, limit_value) VALUES
    ('basic', 'weight_entries', NULL),     -- Unbegrenzt
    ('basic', 'circumference_entries', NULL),
    ('basic', 'caliper_entries', NULL),
    ('basic', 'nutrition_entries', NULL),
    ('basic', 'activity_entries', NULL),
    ('basic', 'photos', 50),
    ('basic', 'ai_calls', 3),              -- 3 KI-Calls/Monat
    ('basic', 'ai_pipeline', 0),           -- Keine Pipeline
    ('basic', 'export_csv', 1),            -- Export erlaubt
    ('basic', 'export_json', 1),
    ('basic', 'export_zip', 1)
ON CONFLICT (tier_id, feature_id) DO NOTHING;

-- PREMIUM tier (alles unbegrenzt)
INSERT INTO tier_limits (tier_id, feature_id, limit_value) VALUES
    ('premium', 'weight_entries', NULL),
    ('premium', 'circumference_entries', NULL),
    ('premium', 'caliper_entries', NULL),
    ('premium', 'nutrition_entries', NULL),
    ('premium', 'activity_entries', NULL),
    ('premium', 'photos', NULL),
    ('premium', 'ai_calls', NULL),         -- Unbegrenzt KI
    ('premium', 'ai_pipeline', 1),         -- Pipeline erlaubt
    ('premium', 'export_csv', 1),
    ('premium', 'export_json', 1),
    ('premium', 'export_zip', 1)
ON CONFLICT (tier_id, feature_id) DO NOTHING;

-- SELFHOSTED tier (alles unbegrenzt)
INSERT INTO tier_limits (tier_id, feature_id, limit_value) VALUES
    ('selfhosted', 'weight_entries', NULL),
    ('selfhosted', 'circumference_entries', NULL),
    ('selfhosted', 'caliper_entries', NULL),
    ('selfhosted', 'nutrition_entries', NULL),
    ('selfhosted', 'activity_entries', NULL),
    ('selfhosted', 'photos', NULL),
    ('selfhosted', 'ai_calls', NULL),
    ('selfhosted', 'ai_pipeline', 1),
    ('selfhosted', 'export_csv', 1),
    ('selfhosted', 'export_json', 1),
    ('selfhosted', 'export_zip', 1)
ON CONFLICT (tier_id, feature_id) DO NOTHING;

-- ============================================================================
-- Migrate existing profiles
-- ============================================================================

-- Lars' Profile → selfhosted tier with email verified
UPDATE profiles
SET
    tier = 'selfhosted',
    email_verified = true
WHERE
    email = 'lars@stommer.com'
    OR role = 'admin';

-- Other existing profiles → free tier, unverified
UPDATE profiles
SET
    tier = 'free',
    email_verified = false
WHERE
    tier IS NULL
    OR tier = '';

-- Initialize user_stats for existing profiles
INSERT INTO user_stats (profile_id, weight_entries_count, photos_count)
SELECT
    p.id,
    (SELECT COUNT(*) FROM weight_log WHERE profile_id = p.id),
    (SELECT COUNT(*) FROM photos WHERE profile_id = p.id)
FROM profiles p
ON CONFLICT (profile_id) DO NOTHING;

-- ============================================================================
-- Create indexes for performance
-- ============================================================================
CREATE INDEX IF NOT EXISTS idx_tier_limits_tier ON tier_limits(tier_id);
CREATE INDEX IF NOT EXISTS idx_tier_limits_feature ON tier_limits(feature_id);
CREATE INDEX IF NOT EXISTS idx_user_restrictions_profile ON user_feature_restrictions(profile_id);
CREATE INDEX IF NOT EXISTS idx_user_usage_profile ON user_feature_usage(profile_id);
CREATE INDEX IF NOT EXISTS idx_access_grants_profile ON access_grants(profile_id, valid_until DESC);
CREATE INDEX IF NOT EXISTS idx_access_grants_active ON access_grants(profile_id, is_active, valid_until DESC);
CREATE INDEX IF NOT EXISTS idx_coupons_code ON coupons(code);
CREATE INDEX IF NOT EXISTS idx_coupon_redemptions_profile ON coupon_redemptions(profile_id);

-- ============================================================================
-- Migration complete
-- ============================================================================
-- Run this migration with:
-- psql -h localhost -U mitai_prod -d mitai_prod < backend/migrations/v9c_subscription_system.sql
--
-- Or via Docker:
-- docker exec -i mitai-postgres psql -U mitai_prod -d mitai_prod < backend/migrations/v9c_subscription_system.sql
-- ============================================================================
