defmodule ErrorTracker.Migration.Postgres.V01 do
  @moduledoc false

  use Ecto.Migration

  import Ecto.Query

  def up(opts = %{create_schema: create_schema, prefix: prefix}) do
    # Prior to V02 the migration version was stored in table comments.
    # As of now the migration version is stored in a new table (created in V02).
    #
    # However, systems migrating to V02 may think they need to run V01 too, so
    # we need to check for the legacy version storage to avoid running this
    # migration twice.
    if current_version_legacy(opts) == 0 do
      if create_schema, do: execute("CREATE SCHEMA IF NOT EXISTS #{prefix}")

      create table(:error_tracker_meta,
               primary_key: [name: :key, type: :string],
               prefix: prefix
             ) do
        add :value, :string, null: false
      end

      create table(:error_tracker_errors,
               primary_key: [name: :id, type: :bigserial],
               prefix: prefix
             ) do
        add :kind, :string, null: false
        add :reason, :text, null: false
        add :source_line, :text, null: false
        add :source_function, :text, null: false
        add :status, :string, null: false
        add :fingerprint, :string, null: false
        add :last_occurrence_at, :utc_datetime_usec, null: false

        timestamps(type: :utc_datetime_usec)
      end

      create unique_index(:error_tracker_errors, [:fingerprint], prefix: prefix)

      create table(:error_tracker_occurrences,
               primary_key: [name: :id, type: :bigserial],
               prefix: prefix
             ) do
        add :context, :map, null: false
        add :reason, :text, null: false
        add :stacktrace, :map, null: false

        add :error_id,
            references(:error_tracker_errors,
              on_delete: :delete_all,
              column: :id,
              type: :bigserial
            ),
            null: false

        timestamps(type: :utc_datetime_usec, updated_at: false)
      end

      create index(:error_tracker_occurrences, [:error_id], prefix: prefix)
    else
      :noop
    end
  end

  def down(%{prefix: prefix}) do
    drop table(:error_tracker_occurrences, prefix: prefix)
    drop table(:error_tracker_errors, prefix: prefix)
    drop_if_exists table(:error_tracker_meta, prefix: prefix)
  end

  def current_version_legacy(opts) do
    query =
      from pg_class in "pg_class",
        left_join: pg_description in "pg_description",
        on: pg_description.objoid == pg_class.oid,
        left_join: pg_namespace in "pg_namespace",
        on: pg_namespace.oid == pg_class.relnamespace,
        where: pg_class.relname == "error_tracker_errors",
        where: pg_namespace.nspname == ^opts.escaped_prefix,
        select: pg_description.description

    case repo().one(query, log: false) do
      version when is_binary(version) -> String.to_integer(version)
      _other -> 0
    end
  end
end
