lib/mix/tasks/pgflow_dashboard.gen.indexes.ex

defmodule Mix.Tasks.PgflowDashboard.Gen.Indexes do
  @shortdoc "Generates optional performance indexes for PgFlow Dashboard"

  @moduledoc """
  Generates an Ecto migration that adds performance indexes to pgflow tables
  for faster dashboard queries.

  ## Usage

      mix pgflow_dashboard.gen.indexes
      mix pgflow_dashboard.gen.indexes --migrations-path priv/repo/migrations

  ## Options

    * `--migrations-path` - Path to the migrations directory.
      Defaults to `priv/repo/migrations`.

  ## Generated Indexes

  This migration creates the following indexes:

    * `idx_runs_started_at` - For time-range filtering
    * `idx_runs_flow_status_started` - For filtered run lists
    * `idx_runs_completed_at` - For duration calculations
    * `idx_step_states_status` - For progress calculations

  ## Note

  These indexes are optional and primarily benefit dashboards with high query load.
  For small deployments, the default indexes may be sufficient.

  ## Example

      # Generate the migration
      $ mix pgflow_dashboard.gen.indexes

      # Run the migration
      $ mix ecto.migrate

  """

  use Mix.Task

  alias Mix.Tasks.Pgflow.Helpers

  @impl Mix.Task
  def run(args) do
    Helpers.write_migration(
      args,
      "add_pgflow_dashboard_indexes",
      &generate_migration_content/1,
      &message/1
    )
  end

  defp message(filepath) do
    """
    Generated migration: #{filepath}

    Run the migration with:
        mix ecto.migrate

    This will create performance indexes for dashboard queries.
    These indexes are optional but recommended for high-traffic dashboards.
    """
  end

  defp generate_migration_content(app_module) do
    """
    defmodule #{app_module}.Repo.Migrations.AddPgflowDashboardIndexes do
      @moduledoc \"\"\"
      Adds optional performance indexes for PgFlow Dashboard queries.

      These indexes optimize:
      - Time-range filtered queries
      - Status-based filtering
      - Progress calculations

      Generated by: mix pgflow_dashboard.gen.indexes
      \"\"\"
      use Ecto.Migration

      @disable_ddl_transaction true
      @disable_migration_lock true

      def up do
        # Index for time-range filtering on runs
        execute \"\"\"
        CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_runs_started_at
        ON pgflow.runs (started_at DESC)
        \"\"\"

        # Composite index for filtered run lists
        execute \"\"\"
        CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_runs_flow_status_started
        ON pgflow.runs (flow_slug, status, started_at DESC)
        \"\"\"

        # Index for completed runs (duration calculations)
        execute \"\"\"
        CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_runs_completed_at
        ON pgflow.runs (completed_at DESC)
        WHERE completed_at IS NOT NULL
        \"\"\"

        # Index for step state status (progress calculations)
        execute \"\"\"
        CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_step_states_status
        ON pgflow.step_states (run_id, status)
        \"\"\"

        # Index for step tasks status
        execute \"\"\"
        CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_step_tasks_status
        ON pgflow.step_tasks (run_id, step_slug, status)
        \"\"\"

        # Index for worker heartbeats
        execute \"\"\"
        CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_workers_heartbeat
        ON pgflow.workers (last_heartbeat_at DESC)
        \"\"\"
      end

      def down do
        execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_workers_heartbeat"
        execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_step_tasks_status"
        execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_step_states_status"
        execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_runs_completed_at"
        execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_runs_flow_status_started"
        execute "DROP INDEX CONCURRENTLY IF EXISTS pgflow.idx_runs_started_at"
      end
    end
    """
  end
end