defmodule Moba.Admin.Skills do
  @moduledoc """
  Admin functions for managing Skills, mostly generated by Torch package.
  """

  alias Moba.{Repo, Game}
  alias Game.Schema.Skill
  alias Game.Query.SkillQuery

  import Ecto.Query, warn: false
  import Torch.Helpers, only: [sort: 1, paginate: 4]
  import Filtrex.Type.Config

  @pagination [page_size: 50]
  @pagination_distance 5

  def paginate(params \\ %{}) do
    params =
      params
      |> Map.put_new("sort_direction", "desc")
      |> Map.put_new("sort_field", "inserted_at")

    {:ok, sort_direction} = Map.fetch(params, "sort_direction")
    {:ok, sort_field} = Map.fetch(params, "sort_field")

    with {:ok, filter} <- Filtrex.parse_params(filter_config(:skills), params["skill"] || %{}),
         %Scrivener.Page{} = page <- do_paginate(filter, params) do
      {:ok,
       %{
         skills: page.entries,
         page_number: page.page_number,
         page_size: page.page_size,
         total_pages: page.total_pages,
         total_entries: page.total_entries,
         distance: @pagination_distance,
         sort_field: sort_field,
         sort_direction: sort_direction
       }}
    else
      {:error, error} -> {:error, error}
      error -> {:error, error}
    end
  end

  def list, do: Repo.all(Skill)

  def list_with_same_code(nil), do: []

  def list_with_same_code(code) do
    from(s in Skill, where: s.code == ^code, where: is_nil(s.resource_uuid), order_by: [asc: s.level])
    |> Repo.all()
  end

  def get!(id), do: Repo.get!(Skill, id)

  def create(attrs \\ %{}) do
    %Skill{}
    |> Skill.changeset(attrs)
    |> Repo.insert()
  end

  def update(%Skill{} = skill, attrs) do
    if attrs["description"] && attrs["effects"] && skill.code do
      query =
        from s in Skill,
          where: s.code == ^skill.code,
          where: s.id != ^skill.id,
          where: is_nil(s.resource_uuid)

      changes = %{
        description: attrs["description"],
        effects: attrs["effects"],
        enabled: attrs["enabled"],
        name: attrs["name"],
        level_requirement: attrs["level_requirement"],
        damage_type: attrs["damage_type"]
      }

      changes = if attrs["image"], do: Map.put(changes, :image, attrs["image"]), else: changes

      query
      |> Repo.all()
      |> Enum.each(fn skill ->
        skill
        |> Skill.changeset(changes)
        |> Repo.update!()
      end)
    end

    skill
    |> Skill.changeset(attrs)
    |> Repo.update()
  end

  def delete(%Skill{} = skill) do
    Repo.delete(skill)
  end

  def change(%Skill{} = skill) do
    Skill.changeset(skill, %{})
  end

  defp do_paginate(filter, params) do
    SkillQuery.canon()
    |> Filtrex.query(filter)
    |> order_by(^sort(params))
    |> paginate(Repo, params, @pagination)
  end

  defp filter_config(:skills) do
    defconfig do
      boolean(:enabled)
      text(:name)
      text(:code)
      text(:damage_type)
      number(:level)
      number(:mp_cost)
      number(:cooldown)
      boolean(:passive)
      boolean(:ultimate)
    end
  end
end
