defmodule Tarakan.Repo.Migrations.AddFindingTargetedTaskKinds do
  use Ecto.Migration

  @old ~w(code_review threat_model privacy_review business_logic verify_findings write_fix)
  @new @old ++ ~w(diff_review refute_finding reproduce_finding calibrate_severity)

  def up do
    alter table(:review_tasks) do
      # refute_finding / reproduce_finding / calibrate_severity all act on one
      # canonical finding rather than a tree, so they need a finding to point at.
      add :target_canonical_finding_id,
          references(:canonical_findings, on_delete: :delete_all)
    end

    create index(:review_tasks, [:target_canonical_finding_id])

    drop constraint(:review_tasks, :review_tasks_kind_must_be_valid)
    create constraint(:review_tasks, :review_tasks_kind_must_be_valid, check: kind_check(@new))

    alter table(:canonical_finding_checks) do
      # An adversarial check was asked to break the finding, not to agree with it.
      # Reusing the existing verdicts keeps the vocabulary small: "confirmed" on
      # an adversarial check means the attack failed and the finding held.
      add :adversarial, :boolean, null: false, default: false
    end

    create index(:canonical_finding_checks, [:canonical_finding_id, :adversarial])
  end

  def down do
    drop index(:canonical_finding_checks, [:canonical_finding_id, :adversarial])

    alter table(:canonical_finding_checks) do
      remove :adversarial
    end

    drop constraint(:review_tasks, :review_tasks_kind_must_be_valid)
    create constraint(:review_tasks, :review_tasks_kind_must_be_valid, check: kind_check(@old))

    drop index(:review_tasks, [:target_canonical_finding_id])

    alter table(:review_tasks) do
      remove :target_canonical_finding_id
    end
  end

  defp kind_check(kinds) do
    list = Enum.map_join(kinds, ", ", &"'#{&1}'")
    "kind IN (#{list})"
  end
end
