-- =====================================================
-- Index Optimization
--
-- Adds partial indexes for common query patterns,
-- covers the advanced search filter combinations,
-- and optimizes reporting queries.
-- =====================================================

-- 1. Active members composite index (most common query pattern)
-- Covers: listMembers, searchMembers, all reporting functions
CREATE INDEX IF NOT EXISTS ix_members_active_account_status
  ON public.members(account_id, status, last_name, first_name)
  WHERE is_archived = false;

-- 2. Entry date range queries (searchMembers with date filters)
CREATE INDEX IF NOT EXISTS ix_members_entry_date
  ON public.members(account_id, entry_date)
  WHERE entry_date IS NOT NULL;

-- 3. Dues category filter (searchMembers)
CREATE INDEX IF NOT EXISTS ix_members_dues_category
  ON public.members(account_id, dues_category_id)
  WHERE dues_category_id IS NOT NULL;

-- 4. Boolean flag filters (searchMembers flag queries)
-- Partial indexes only store rows where the flag is true (very compact)
CREATE INDEX IF NOT EXISTS ix_members_honorary
  ON public.members(account_id) WHERE is_honorary = true;
CREATE INDEX IF NOT EXISTS ix_members_youth
  ON public.members(account_id) WHERE is_youth = true;
CREATE INDEX IF NOT EXISTS ix_members_founding
  ON public.members(account_id) WHERE is_founding_member = true;
CREATE INDEX IF NOT EXISTS ix_members_retiree
  ON public.members(account_id) WHERE is_retiree = true;

-- 5. Active SEPA mandates lookup (finance integration)
CREATE INDEX IF NOT EXISTS ix_sepa_mandates_active_lookup
  ON public.sepa_mandates(member_id, status)
  WHERE status = 'active' AND is_primary = true;

-- 6. Communications per member (timeline queries)
CREATE INDEX IF NOT EXISTS ix_member_comms_member_date
  ON public.member_communications(member_id, created_at DESC);

-- 7. Audit log: action-type filtering (timeline with action filter)
CREATE INDEX IF NOT EXISTS ix_member_audit_member_action
  ON public.member_audit_log(member_id, action, created_at DESC);

-- 8. Tag assignments: member lookup (for search filter + detail view)
CREATE INDEX IF NOT EXISTS ix_tag_assignments_member
  ON public.member_tag_assignments(member_id);

-- 9. Reporting: active members for retention/duration CROSS JOIN
-- Column order: account_id first (equality), then date columns (range scans)
-- is_archived excluded from key since it's in WHERE clause
CREATE INDEX IF NOT EXISTS ix_members_active_reporting
  ON public.members(account_id, entry_date, exit_date, status)
  WHERE is_archived = false;

-- 10. Member merge log: primary member lookup
CREATE INDEX IF NOT EXISTS ix_member_merges_primary
  ON public.member_merges(primary_member_id);

-- 11. GDPR: candidates for anonymization (batch enforcement query)
-- status excluded from key since enforcement query uses dynamic ANY(array)
-- Covers: WHERE account_id = ? AND exit_date IS NOT NULL AND exit_date + interval <= current_date
CREATE INDEX IF NOT EXISTS ix_members_gdpr_candidates
  ON public.members(account_id, exit_date)
  WHERE exit_date IS NOT NULL AND is_archived = false AND first_name != 'ANONYMISIERT';

-- 12. Portal invitations: account listing (listPortalInvitations query)
CREATE INDEX IF NOT EXISTS ix_portal_invitations_account_date
  ON public.member_portal_invitations(account_id, created_at DESC);

-- 13. Department assignments by department (searchMembers department filter subquery)
CREATE INDEX IF NOT EXISTS ix_dept_assignments_department
  ON public.member_department_assignments(department_id, member_id);
