defmodule Mobilizon.Storage.Repo.Migrations.CleanupPosts do
  use Ecto.Migration

  def up do
    # Make sure we don't have any duplicate posts
    rows = fetch_bad_rows()
    Enum.each(rows, &process_row/1)
  end

  def down do
    # No way down
  end

  defp fetch_bad_rows() do
    %Postgrex.Result{rows: rows} =
      Ecto.Adapters.SQL.query!(
        Mobilizon.Storage.Repo,
        "SELECT * FROM (
          SELECT id, url,
          ROW_NUMBER() OVER(PARTITION BY url ORDER BY inserted_at asc) AS Row
          FROM posts
        ) dups
        WHERE dups.Row > 1;"
      )

    rows
  end

  defp process_row([id, url, _row]) do
    first_id = find_first_post_id(url)

    if id != first_id do
      repair_post_medias(id, first_id)
      repair_post_tags(id, first_id)
      delete_row(id)
    end
  end

  defp find_first_post_id(url) do
    %Postgrex.Result{rows: [[id]]} =
      Ecto.Adapters.SQL.query!(
        Mobilizon.Storage.Repo,
        "SELECT id FROM posts WHERE url = $1 order by inserted_at asc limit 1",
        [url]
      )

    id
  end

  defp repair_post_medias(id, first_id) do
    Ecto.Adapters.SQL.query!(
      Mobilizon.Storage.Repo,
      "UPDATE posts_medias SET post_id = $1 WHERE post_id = $2",
      [first_id, id]
    )
  end

  defp repair_post_tags(id, first_id) do
    Ecto.Adapters.SQL.query!(
      Mobilizon.Storage.Repo,
      "UPDATE posts_tags SET post_id = $1 WHERE post_id = $2",
      [first_id, id]
    )
  end

  defp delete_row(id) do
    Ecto.Adapters.SQL.query!(
      Mobilizon.Storage.Repo,
      "DELETE FROM posts WHERE id = $1",
      [id]
    )
  end
end