-- Monaco USA Portal - Post-Deployment Database Fixes
-- This script is IDEMPOTENT - safe to run multiple times.
-- Run after `docker compose up` once all containers are healthy.
--
-- Handles:
--   1. Storage RLS policies (storage-api creates tables AFTER db init)
--   2. Service role access grants
--   3. Incremental migrations for existing databases
--   4. Notifications table (if missing)
-- ============================================================================

-- ============================================
-- 1. STORAGE POLICIES
--    storage-api creates storage.objects and storage.buckets with RLS enabled
--    but no policies. We need to add service_role policies so the portal
--    can upload/delete files via supabaseAdmin.
-- ============================================

DO $$
BEGIN
  -- Ensure service_role has BYPASSRLS
  IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'service_role' AND NOT rolbypassrls) THEN
    ALTER ROLE service_role BYPASSRLS;
  END IF;
EXCEPTION
  WHEN insufficient_privilege THEN
    RAISE NOTICE 'Could not grant BYPASSRLS to service_role - will rely on explicit policies';
END $$;

-- storage.objects policies
DO $$
BEGIN
  IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'storage' AND table_name = 'objects') THEN
    -- Drop and recreate to ensure clean state
    DROP POLICY IF EXISTS "service_role_all_select" ON storage.objects;
    DROP POLICY IF EXISTS "service_role_all_insert" ON storage.objects;
    DROP POLICY IF EXISTS "service_role_all_update" ON storage.objects;
    DROP POLICY IF EXISTS "service_role_all_delete" ON storage.objects;

    CREATE POLICY "service_role_all_select" ON storage.objects
    FOR SELECT TO service_role USING (true);
    CREATE POLICY "service_role_all_insert" ON storage.objects
    FOR INSERT TO service_role WITH CHECK (true);
    CREATE POLICY "service_role_all_update" ON storage.objects
    FOR UPDATE TO service_role USING (true);
    CREATE POLICY "service_role_all_delete" ON storage.objects
    FOR DELETE TO service_role USING (true);

    -- Public read access for avatars and event images
    DROP POLICY IF EXISTS "public_read_avatars" ON storage.objects;
    CREATE POLICY "public_read_avatars" ON storage.objects
    FOR SELECT USING (bucket_id IN ('avatars', 'event-images'));

    -- Authenticated users can read documents
    DROP POLICY IF EXISTS "authenticated_read_documents" ON storage.objects;
    CREATE POLICY "authenticated_read_documents" ON storage.objects
    FOR SELECT TO authenticated USING (bucket_id = 'documents');

    GRANT ALL ON storage.objects TO service_role;

    RAISE NOTICE 'storage.objects policies applied';
  ELSE
    RAISE NOTICE 'storage.objects table not found - storage-api may not have started yet';
  END IF;
END $$;

-- storage.buckets policies
DO $$
BEGIN
  IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'storage' AND table_name = 'buckets') THEN
    DROP POLICY IF EXISTS "service_role_all_buckets_select" ON storage.buckets;
    DROP POLICY IF EXISTS "service_role_all_buckets_insert" ON storage.buckets;
    DROP POLICY IF EXISTS "service_role_all_buckets_update" ON storage.buckets;
    DROP POLICY IF EXISTS "service_role_all_buckets_delete" ON storage.buckets;

    CREATE POLICY "service_role_all_buckets_select" ON storage.buckets
    FOR SELECT TO service_role USING (true);
    CREATE POLICY "service_role_all_buckets_insert" ON storage.buckets
    FOR INSERT TO service_role WITH CHECK (true);
    CREATE POLICY "service_role_all_buckets_update" ON storage.buckets
    FOR UPDATE TO service_role USING (true);
    CREATE POLICY "service_role_all_buckets_delete" ON storage.buckets
    FOR DELETE TO service_role USING (true);

    -- Allow authenticated users to read bucket info (needed for uploads)
    DROP POLICY IF EXISTS "authenticated_read_buckets" ON storage.buckets;
    CREATE POLICY "authenticated_read_buckets" ON storage.buckets
    FOR SELECT TO authenticated USING (true);

    GRANT ALL ON storage.buckets TO service_role;

    RAISE NOTICE 'storage.buckets policies applied';
  ELSE
    RAISE NOTICE 'storage.buckets table not found - storage-api may not have started yet';
  END IF;
END $$;

-- Ensure schema and general grants
GRANT USAGE ON SCHEMA storage TO service_role;
GRANT USAGE ON SCHEMA storage TO authenticated;

-- ============================================
-- 2. STORAGE BUCKETS
--    Ensure required buckets exist
-- ============================================

DO $$
BEGIN
  IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'storage' AND table_name = 'buckets') THEN
    INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
    VALUES (
      'avatars', 'avatars', true, 5242880,
      ARRAY['image/jpeg', 'image/png', 'image/webp', 'image/gif']
    ) ON CONFLICT (id) DO UPDATE SET public = true, file_size_limit = EXCLUDED.file_size_limit, allowed_mime_types = EXCLUDED.allowed_mime_types;

    INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
    VALUES (
      'documents', 'documents', true, 52428800,
      ARRAY['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'text/plain', 'text/csv', 'application/json', 'image/jpeg', 'image/png', 'image/webp', 'image/gif']
    ) ON CONFLICT (id) DO UPDATE SET public = true, file_size_limit = EXCLUDED.file_size_limit, allowed_mime_types = EXCLUDED.allowed_mime_types;

    INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
    VALUES (
      'event-images', 'event-images', true, 10485760,
      ARRAY['image/jpeg', 'image/png', 'image/webp']
    ) ON CONFLICT (id) DO UPDATE SET public = true, file_size_limit = EXCLUDED.file_size_limit, allowed_mime_types = EXCLUDED.allowed_mime_types;

    RAISE NOTICE 'Storage buckets ensured';
  END IF;
END $$;

-- ============================================
-- 3. NOTIFICATIONS TABLE (added post-migration-016)
-- ============================================

CREATE TABLE IF NOT EXISTS public.notifications (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  member_id UUID NOT NULL REFERENCES public.members(id) ON DELETE CASCADE,
  type TEXT NOT NULL CHECK (type IN ('welcome', 'event', 'payment', 'membership', 'system', 'announcement')),
  title TEXT NOT NULL,
  message TEXT NOT NULL,
  link TEXT,
  read_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_notifications_member ON public.notifications(member_id);
CREATE INDEX IF NOT EXISTS idx_notifications_unread ON public.notifications(member_id) WHERE read_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_notifications_created ON public.notifications(created_at DESC);

ALTER TABLE public.notifications ENABLE ROW LEVEL SECURITY;

-- Idempotent policy creation
DO $$
BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polrelid = 'public.notifications'::regclass AND polname = 'Members can view own notifications') THEN
    CREATE POLICY "Members can view own notifications"
      ON public.notifications FOR SELECT TO authenticated
      USING (member_id = auth.uid());
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polrelid = 'public.notifications'::regclass AND polname = 'Members can update own notifications') THEN
    CREATE POLICY "Members can update own notifications"
      ON public.notifications FOR UPDATE TO authenticated
      USING (member_id = auth.uid());
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polrelid = 'public.notifications'::regclass AND polname = 'Admin can manage all notifications') THEN
    CREATE POLICY "Admin can manage all notifications"
      ON public.notifications FOR ALL TO authenticated
      USING (EXISTS (SELECT 1 FROM public.members WHERE id = auth.uid() AND role = 'admin'))
      WITH CHECK (EXISTS (SELECT 1 FROM public.members WHERE id = auth.uid() AND role = 'admin'));
  END IF;
END $$;

GRANT SELECT, UPDATE ON public.notifications TO authenticated;
GRANT ALL ON public.notifications TO service_role;

-- Welcome notification trigger
CREATE OR REPLACE FUNCTION create_welcome_notification()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO public.notifications (member_id, type, title, message, link)
  VALUES (
    NEW.id, 'welcome',
    'Welcome to Monaco USA!',
    'Thank you for joining our community. Complete your profile and explore upcoming events.',
    '/profile'
  );
  RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

DROP TRIGGER IF EXISTS on_member_created_notification ON public.members;
CREATE TRIGGER on_member_created_notification
  AFTER INSERT ON public.members
  FOR EACH ROW
  EXECUTE FUNCTION create_welcome_notification();

-- ============================================
-- 4. MIGRATIONS 017-025 (idempotent)
-- ============================================

-- --- Migration 017: Fix RLS role escalation ---
-- (Superseded by 019, kept for reference only)

-- --- Migration 018: Atomic Member ID Generation ---
DO $$
DECLARE
  max_num INTEGER;
BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_sequences WHERE schemaname = 'public' AND sequencename = 'member_id_seq') THEN
    SELECT COALESCE(
      MAX(
        CAST(
          SUBSTRING(member_id FROM '[0-9]+$') AS INTEGER
        )
      ),
      0
    ) INTO max_num
    FROM public.members;

    EXECUTE format('CREATE SEQUENCE member_id_seq START WITH %s', max_num + 1);
    RAISE NOTICE 'Created member_id_seq starting at %', max_num + 1;
  END IF;
END $$;

CREATE OR REPLACE FUNCTION generate_member_id()
RETURNS TRIGGER AS $$
DECLARE
  next_num INTEGER;
  current_year TEXT;
BEGIN
  IF NEW.member_id IS NOT NULL THEN
    RETURN NEW;
  END IF;
  next_num := NEXTVAL('member_id_seq');
  current_year := EXTRACT(YEAR FROM CURRENT_DATE)::TEXT;
  NEW.member_id := 'MUSA-' || current_year || '-' || LPAD(next_num::TEXT, 4, '0');
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS set_member_id ON public.members;
CREATE TRIGGER set_member_id
  BEFORE INSERT ON public.members
  FOR EACH ROW
  WHEN (NEW.member_id IS NULL)
  EXECUTE FUNCTION generate_member_id();

CREATE INDEX IF NOT EXISTS idx_members_member_id ON public.members(member_id);

-- --- Migration 019: Fix admin role assignment ---
DROP POLICY IF EXISTS "Users can update own profile" ON public.members;
DROP POLICY IF EXISTS "Members can update own non-role fields" ON public.members;
DROP POLICY IF EXISTS "Members can update own profile" ON public.members;
DROP POLICY IF EXISTS "Admins can update other members" ON public.members;

CREATE POLICY "Members can update own profile"
  ON public.members
  FOR UPDATE
  TO authenticated
  USING (auth.uid() = id)
  WITH CHECK (
    auth.uid() = id
    AND role = (SELECT role FROM public.members WHERE id = auth.uid())
  );

CREATE POLICY "Admins can update other members"
  ON public.members
  FOR UPDATE
  TO authenticated
  USING (
    EXISTS (
      SELECT 1 FROM public.members
      WHERE id = auth.uid() AND role = 'admin'
    )
  )
  WITH CHECK (
    (id != auth.uid())
    OR
    (id = auth.uid() AND role = (SELECT role FROM public.members WHERE id = auth.uid()))
  );

-- --- Migration 020: Approval workflow ---
ALTER TABLE public.members ADD COLUMN IF NOT EXISTS approved_at TIMESTAMPTZ;
ALTER TABLE public.members ADD COLUMN IF NOT EXISTS approved_by UUID REFERENCES auth.users(id);
ALTER TABLE public.members ADD COLUMN IF NOT EXISTS rejected_at TIMESTAMPTZ;
ALTER TABLE public.members ADD COLUMN IF NOT EXISTS rejected_by UUID REFERENCES auth.users(id);
ALTER TABLE public.members ADD COLUMN IF NOT EXISTS rejection_reason TEXT;

INSERT INTO public.email_templates (template_key, template_name, category, subject, body_html, body_text, is_active)
VALUES
  ('member_approved', 'Member Approved', 'membership', 'Welcome to Monaco USA - Membership Approved!',
   '<h2 style="color: #22c55e; text-align: center;">Membership Approved!</h2>
    <p style="color: #334155; line-height: 1.6;">Dear {{first_name}},</p>
    <p style="color: #334155; line-height: 1.6;">We are pleased to inform you that your membership application to Monaco USA has been <strong>approved</strong>!</p>
    <p style="color: #334155; line-height: 1.6;">Your member ID is: <strong>{{member_id}}</strong></p>
    <p style="color: #334155; line-height: 1.6;">You now have full access to the member portal, including events, documents, and the member directory.</p>
    <div style="text-align: center; margin: 24px 0;">
      <a href="{{site_url}}/dashboard" style="background: #CE1126; color: white; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: bold;">Visit Your Dashboard</a>
    </div>',
   'Dear {{first_name}}, Your membership to Monaco USA has been approved! Your member ID is {{member_id}}.',
   true),
  ('member_rejected', 'Member Rejected', 'membership', 'Monaco USA - Membership Application Update',
   '<h2 style="color: #ef4444; text-align: center;">Application Update</h2>
    <p style="color: #334155; line-height: 1.6;">Dear {{first_name}},</p>
    <p style="color: #334155; line-height: 1.6;">Thank you for your interest in Monaco USA. After careful review, we regret to inform you that your membership application was not approved at this time.</p>
    <p style="color: #334155; line-height: 1.6;">{{reason}}</p>
    <p style="color: #334155; line-height: 1.6;">If you have questions, please contact us at info@monacousa.org.</p>',
   'Dear {{first_name}}, Thank you for your interest in Monaco USA. After review, your membership application was not approved at this time.',
   true)
ON CONFLICT (template_key) DO NOTHING;

-- --- Migration 021: RSVP deadlines ---
ALTER TABLE public.events ADD COLUMN IF NOT EXISTS rsvp_deadline TIMESTAMPTZ;
ALTER TABLE public.events ADD COLUMN IF NOT EXISTS rsvp_deadline_enabled BOOLEAN DEFAULT FALSE;

-- --- Migration 022: Directory privacy ---
ALTER TABLE public.members
ADD COLUMN IF NOT EXISTS directory_privacy JSONB DEFAULT '{
  "show_email": true,
  "show_phone": true,
  "show_address": false,
  "show_nationality": true
}'::jsonb;

-- --- Migration 023: Document full-text search ---
ALTER TABLE public.documents
ADD COLUMN IF NOT EXISTS search_vector tsvector;

UPDATE public.documents
SET search_vector = to_tsvector('english',
  coalesce(title, '') || ' ' ||
  coalesce(description, '') || ' ' ||
  coalesce(file_name, '')
)
WHERE search_vector IS NULL;

CREATE INDEX IF NOT EXISTS idx_documents_search
ON public.documents USING GIN (search_vector);

CREATE OR REPLACE FUNCTION update_document_search_vector()
RETURNS TRIGGER AS $$
BEGIN
  NEW.search_vector := to_tsvector('english',
    coalesce(NEW.title, '') || ' ' ||
    coalesce(NEW.description, '') || ' ' ||
    coalesce(NEW.file_name, '')
  );
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trg_update_document_search ON public.documents;
CREATE TRIGGER trg_update_document_search
  BEFORE INSERT OR UPDATE OF title, description, file_name
  ON public.documents
  FOR EACH ROW
  EXECUTE FUNCTION update_document_search_vector();

-- --- Migration 024: Cron execution logs ---
CREATE TABLE IF NOT EXISTS public.cron_execution_logs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  job_name TEXT NOT NULL,
  status TEXT NOT NULL DEFAULT 'running' CHECK (status IN ('running', 'completed', 'failed')),
  started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  completed_at TIMESTAMPTZ,
  duration_ms INTEGER,
  result JSONB,
  error_message TEXT,
  triggered_by TEXT DEFAULT 'cron'
);

CREATE INDEX IF NOT EXISTS idx_cron_logs_job_name ON public.cron_execution_logs (job_name, started_at DESC);
CREATE INDEX IF NOT EXISTS idx_cron_logs_started ON public.cron_execution_logs (started_at DESC);

ALTER TABLE public.cron_execution_logs ENABLE ROW LEVEL SECURITY;

DO $$
BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polrelid = 'public.cron_execution_logs'::regclass AND polname = 'Admins can read cron logs') THEN
    CREATE POLICY "Admins can read cron logs"
      ON public.cron_execution_logs FOR SELECT TO authenticated
      USING (EXISTS (SELECT 1 FROM public.members WHERE members.id = auth.uid() AND members.role = 'admin'));
  END IF;
  IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polrelid = 'public.cron_execution_logs'::regclass AND polname = 'Service role can manage cron logs') THEN
    CREATE POLICY "Service role can manage cron logs"
      ON public.cron_execution_logs FOR ALL TO service_role
      USING (true) WITH CHECK (true);
  END IF;
END $$;

-- --- Migration 025: Bulk email broadcasts ---
CREATE TABLE IF NOT EXISTS public.bulk_emails (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  subject TEXT NOT NULL,
  body TEXT NOT NULL,
  recipient_filter JSONB DEFAULT '{"target": "all"}'::jsonb,
  total_recipients INTEGER DEFAULT 0,
  sent_count INTEGER DEFAULT 0,
  failed_count INTEGER DEFAULT 0,
  status TEXT NOT NULL DEFAULT 'draft' CHECK (status IN ('draft', 'sending', 'completed', 'failed')),
  sent_by UUID REFERENCES auth.users(id),
  sent_by_name TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  sent_at TIMESTAMPTZ,
  completed_at TIMESTAMPTZ
);

CREATE INDEX IF NOT EXISTS idx_bulk_emails_created ON public.bulk_emails (created_at DESC);

ALTER TABLE public.bulk_emails ENABLE ROW LEVEL SECURITY;

DO $$
BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polrelid = 'public.bulk_emails'::regclass AND polname = 'Admins can manage bulk emails') THEN
    CREATE POLICY "Admins can manage bulk emails"
      ON public.bulk_emails FOR ALL TO authenticated
      USING (EXISTS (SELECT 1 FROM public.members WHERE members.id = auth.uid() AND members.role = 'admin'));
  END IF;
  IF NOT EXISTS (SELECT 1 FROM pg_policy WHERE polrelid = 'public.bulk_emails'::regclass AND polname = 'Service role full access to bulk emails') THEN
    CREATE POLICY "Service role full access to bulk emails"
      ON public.bulk_emails FOR ALL TO service_role
      USING (true) WITH CHECK (true);
  END IF;
END $$;

GRANT ALL ON public.cron_execution_logs TO service_role;
GRANT ALL ON public.bulk_emails TO service_role;

-- ============================================
-- 5. ENSURE SERVICE_ROLE ACCESS TO ALL TABLES
-- ============================================

GRANT ALL ON ALL TABLES IN SCHEMA public TO service_role;
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO service_role;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO service_role;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role;

-- ============================================
-- DONE
-- ============================================
DO $$ BEGIN RAISE NOTICE '=== Post-deploy script completed successfully ==='; END $$;
