defmodule Operately.Data.Change066UpdateTimeframeInGoalActivities.GoalEditing do
  import Ecto.Query
  alias Operately.Repo
  alias Operately.Activities.Activity

  def run do
    goal_editing_activities = Repo.all(
      from a in Activity,
      where: a.action == "goal_editing",
      select: a
    )

    {success_count, error_count} = Enum.reduce(goal_editing_activities, {0, 0}, fn activity, {success, error} ->
      case update_activity_timeframes(activity) do
        {:ok, _} -> {success + 1, error}
        {:error, _} -> {success, error + 1}
      end
    end)

    {:ok, %{success_count: success_count, error_count: error_count}}
  end

  defp update_activity_timeframes(activity) do
    content = activity.content

    previous_timeframe =
      if Map.has_key?(content, "previous_timeframe") do
        content["previous_timeframe"]
      else
        convert_old_timeframe(content["old_timeframe"])
      end

    current_timeframe =
      if Map.has_key?(content, "current_timeframe") do
        content["current_timeframe"]
      else
        convert_old_timeframe(content["new_timeframe"])
      end

    updated_content =
      content
      |> update_timeframe_with_contextual_dates(previous_timeframe, "previous_timeframe")
      |> update_timeframe_with_contextual_dates(current_timeframe, "current_timeframe")

    activity
    |> Activity.changeset(%{content: updated_content})
    |> Repo.update()
  end

  defp update_timeframe_with_contextual_dates(content, nil, _field_name), do: content
  defp update_timeframe_with_contextual_dates(content, timeframe, field_name) do
    start_date = get_date_from_timeframe(timeframe, "start_date")
    end_date = get_date_from_timeframe(timeframe, "end_date")

    contextual_start_date = %{
      date_type: :day,
      value: Calendar.strftime(start_date, "%b %d, %Y"),
      date: start_date
    }

    contextual_end_date = %{
      date_type: :day,
      value: Calendar.strftime(end_date, "%b %d, %Y"),
      date: end_date
    }

    updated_timeframe = Map.merge(timeframe, %{
      "contextual_start_date" => contextual_start_date,
      "contextual_end_date" => contextual_end_date
    })

    Map.put(content, field_name, updated_timeframe)
  end

  defp get_date_from_timeframe(timeframe, field) when is_map(timeframe) do
    cond do
      Map.has_key?(timeframe, String.to_atom(field)) -> Map.get(timeframe, String.to_atom(field))
      Map.has_key?(timeframe, field) -> Map.get(timeframe, field)
      true -> nil
    end
    |> ensure_date()
  end
  defp get_date_from_timeframe(_timeframe, _field), do: nil

  defp ensure_date(%Date{} = date), do: date
  defp ensure_date(date) when is_binary(date) do
    case Date.from_iso8601(date) do
      {:ok, date_struct} -> date_struct
      _ -> nil
    end
  end
  defp ensure_date(_), do: nil

  def convert_old_timeframe(old_timeframe) do
    parts = String.split(old_timeframe, " ")

    if Enum.count(parts) == 1 do
      [year] = parts

      %{
        start_date: Date.from_iso8601!("#{year}-01-01"),
        end_date: Date.from_iso8601!("#{year}-12-31"),
        type: "year"
      }
    else
      [quarter, year] = parts

      [start_date, end_date] =
        case quarter do
          "Q1" -> ["01-01", "03-31"]
          "Q2" -> ["04-01", "06-30"]
          "Q3" -> ["07-01", "09-30"]
          "Q4" -> ["10-01", "12-31"]
        end

      %{
        start_date: Date.from_iso8601!("#{year}-#{start_date}"),
        end_date: Date.from_iso8601!("#{year}-#{end_date}"),
        type: "quarter"
      }
    end
  end
end
