-- Migration V009: Activity Table Partitioning
-- Created: 2025-10-03
-- Description: Convert activities table to partitioned table by month for better performance at scale

-- Step 1: Create new partitioned table
CREATE TABLE IF NOT EXISTS activities_partitioned (
  id UUID NOT NULL,
  child_id UUID NOT NULL,
  user_id UUID NOT NULL,
  activity_type VARCHAR(50) NOT NULL,
  timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  start_time TIMESTAMP,
  end_time TIMESTAMP,
  duration_minutes INTEGER,
  amount DECIMAL(10, 2),
  unit VARCHAR(20),
  notes TEXT,
  metadata JSONB,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id, timestamp)
) PARTITION BY RANGE (timestamp);

-- Step 2: Create partitions for the last 6 months and next 6 months
-- Format: activities_YYYY_MM
DO $$
DECLARE
  start_date DATE;
  end_date DATE;
  partition_name TEXT;
  partition_start DATE;
  partition_end DATE;
BEGIN
  -- Create partitions for last 6 months
  FOR i IN -6..6 LOOP
    partition_start := DATE_TRUNC('month', CURRENT_DATE + (i || ' months')::INTERVAL);
    partition_end := partition_start + INTERVAL '1 month';
    partition_name := 'activities_' || TO_CHAR(partition_start, 'YYYY_MM');

    EXECUTE format(
      'CREATE TABLE IF NOT EXISTS %I PARTITION OF activities_partitioned
       FOR VALUES FROM (%L) TO (%L)',
      partition_name,
      partition_start,
      partition_end
    );

    RAISE NOTICE 'Created partition: %', partition_name;
  END LOOP;
END $$;

-- Step 3: Create indexes on partitions (will be inherited by all partitions)
CREATE INDEX IF NOT EXISTS idx_activities_partitioned_child_id
  ON activities_partitioned(child_id);

CREATE INDEX IF NOT EXISTS idx_activities_partitioned_user_id
  ON activities_partitioned(user_id);

CREATE INDEX IF NOT EXISTS idx_activities_partitioned_activity_type
  ON activities_partitioned(activity_type);

CREATE INDEX IF NOT EXISTS idx_activities_partitioned_timestamp
  ON activities_partitioned(timestamp DESC);

-- Step 4: Create foreign key constraints
ALTER TABLE activities_partitioned
  ADD CONSTRAINT fk_activities_partitioned_child
  FOREIGN KEY (child_id) REFERENCES children(id) ON DELETE CASCADE;

ALTER TABLE activities_partitioned
  ADD CONSTRAINT fk_activities_partitioned_user
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;

-- Step 5: Migrate existing data (if any) from old activities table to partitioned table
INSERT INTO activities_partitioned
SELECT * FROM activities
ON CONFLICT DO NOTHING;

-- Step 6: Rename old table and replace with partitioned table
ALTER TABLE activities RENAME TO activities_old;
ALTER TABLE activities_partitioned RENAME TO activities;

-- Step 7: Create trigger for updated_at
CREATE OR REPLACE FUNCTION update_activities_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = CURRENT_TIMESTAMP;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_update_activities_updated_at
  BEFORE UPDATE ON activities
  FOR EACH ROW
  EXECUTE FUNCTION update_activities_updated_at();

-- Step 8: Create function to automatically create future partitions
CREATE OR REPLACE FUNCTION create_monthly_partition()
RETURNS void AS $$
DECLARE
  partition_date DATE;
  partition_name TEXT;
  partition_start DATE;
  partition_end DATE;
BEGIN
  -- Create partition for next month if it doesn't exist
  partition_date := DATE_TRUNC('month', CURRENT_DATE + INTERVAL '2 months');
  partition_name := 'activities_' || TO_CHAR(partition_date, 'YYYY_MM');

  -- Check if partition exists
  IF NOT EXISTS (
    SELECT 1 FROM pg_class WHERE relname = partition_name
  ) THEN
    partition_start := partition_date;
    partition_end := partition_start + INTERVAL '1 month';

    EXECUTE format(
      'CREATE TABLE IF NOT EXISTS %I PARTITION OF activities
       FOR VALUES FROM (%L) TO (%L)',
      partition_name,
      partition_start,
      partition_end
    );

    RAISE NOTICE 'Auto-created partition: %', partition_name;
  END IF;
END;
$$ LANGUAGE plpgsql;

-- Step 9: Create scheduled job to create partitions (optional - can be done via cron or app)
-- This would typically be called monthly by a cron job or scheduled task
-- Example: SELECT create_monthly_partition();

-- Step 10: Add comments
COMMENT ON TABLE activities IS 'Partitioned activities table - partitioned by month for performance';
COMMENT ON FUNCTION create_monthly_partition() IS 'Auto-creates next month partition if it does not exist';

-- Note: To drop old activities_old table after verification:
-- DROP TABLE IF EXISTS activities_old CASCADE;
