defmodule MvWeb.Helpers.MembershipFeeHelpers do
  @moduledoc """
  Helper functions for membership fee UI components.

  Provides formatting and utility functions for displaying membership fee
  information in LiveViews and templates.
  """

  use Gettext, backend: MvWeb.Gettext

  alias Mv.Membership.Member
  alias Mv.MembershipFees.CalendarCycles
  alias Mv.MembershipFees.MembershipFeeCycle

  @doc """
  Formats a decimal amount as currency string.

  ## Examples

      iex> MvWeb.Helpers.MembershipFeeHelpers.format_currency(Decimal.new("60.00"))
      "60,00 €"

      iex> MvWeb.Helpers.MembershipFeeHelpers.format_currency(Decimal.new("5.5"))
      "5,50 €"
  """
  @spec format_currency(Decimal.t()) :: String.t()
  def format_currency(%Decimal{} = amount) do
    # Use German format: comma as decimal separator, always 2 decimal places
    normalized = Decimal.round(amount, 2)
    normalized_str = Decimal.to_string(normalized, :normal)

    format_currency_parts(normalized_str)
  end

  # Formats currency string with comma as decimal separator
  defp format_currency_parts(normalized_str) do
    case String.split(normalized_str, ".") do
      [int_part, dec_part] ->
        format_with_decimal_part(int_part, dec_part)

      [int_part] ->
        "#{int_part},00 €"

      _ ->
        # Fallback for unexpected split results
        "#{String.replace(normalized_str, ".", ",")} €"
    end
  end

  # Formats currency with decimal part, ensuring exactly 2 decimal places
  defp format_with_decimal_part(int_part, dec_part) do
    dec_size = byte_size(dec_part)

    formatted_dec =
      cond do
        dec_size == 1 -> "#{dec_part}0"
        dec_size == 2 -> dec_part
        dec_size > 2 -> String.slice(dec_part, 0, 2)
        true -> "00"
      end

    "#{int_part},#{formatted_dec} €"
  end

  @doc """
  Formats an interval atom as a translated string.

  ## Examples

      iex> MvWeb.Helpers.MembershipFeeHelpers.format_interval(:monthly)
      "Monthly"

      iex> MvWeb.Helpers.MembershipFeeHelpers.format_interval(:yearly)
      "Yearly"
  """
  @spec format_interval(:monthly | :quarterly | :half_yearly | :yearly) :: String.t()
  def format_interval(:monthly), do: gettext("Monthly")
  def format_interval(:quarterly), do: gettext("Quarterly")
  def format_interval(:half_yearly), do: gettext("Half-yearly")
  def format_interval(:yearly), do: gettext("Yearly")

  @doc """
  Formats a cycle date range as a string.

  Calculates the cycle end date from cycle_start and interval, then formats
  both dates in European format (dd.mm.yyyy).

  ## Examples

      iex> cycle_start = ~D[2024-01-01]
      iex> MvWeb.Helpers.MembershipFeeHelpers.format_cycle_range(cycle_start, :yearly)
      "01.01.2024 - 31.12.2024"

      iex> cycle_start = ~D[2024-03-01]
      iex> MvWeb.Helpers.MembershipFeeHelpers.format_cycle_range(cycle_start, :monthly)
      "01.03.2024 - 31.03.2024"
  """
  @spec format_cycle_range(Date.t(), :monthly | :quarterly | :half_yearly | :yearly) :: String.t()
  def format_cycle_range(cycle_start, interval) do
    cycle_end = CalendarCycles.calculate_cycle_end(cycle_start, interval)
    start_str = format_date(cycle_start)
    end_str = format_date(cycle_end)
    "#{start_str} - #{end_str}"
  end

  @doc """
  Gets the last completed cycle for a member.

  Returns the cycle that was most recently completed (ended before today).
  Returns `nil` if no completed cycles exist.

  ## Parameters

    - `member` - Member struct with loaded membership_fee_cycles and membership_fee_type
    - `today` - Optional date to use as reference (defaults to today)

  ## Returns

    - `%MembershipFeeCycle{}` if found
    - `nil` if no completed cycle exists

  ## Examples

      # Member with cycles from 2023 and 2024, today is 2025-01-15
      iex> cycle = MvWeb.Helpers.MembershipFeeHelpers.get_last_completed_cycle(member)
      # => %MembershipFeeCycle{cycle_start: ~D[2024-01-01], ...}
  """
  @spec get_last_completed_cycle(Member.t() | nil, Date.t() | nil) :: MembershipFeeCycle.t() | nil
  def get_last_completed_cycle(member, today \\ nil)

  def get_last_completed_cycle(nil, _today), do: nil

  def get_last_completed_cycle(%Member{} = member, today) do
    today = today || Date.utc_today()

    case member.membership_fee_type do
      nil ->
        nil

      fee_type ->
        cycles = member.membership_fee_cycles || []

        # Get all completed cycles (cycle_end < today)
        completed_cycles =
          cycles
          |> Enum.filter(fn cycle ->
            cycle_end = CalendarCycles.calculate_cycle_end(cycle.cycle_start, fee_type.interval)
            Date.compare(today, cycle_end) == :gt
          end)

        # Return the most recent completed cycle (highest cycle_start)
        completed_cycles
        |> Enum.max_by(& &1.cycle_start, Date, fn -> nil end)
    end
  end

  @doc """
  Gets the current cycle for a member.

  Returns the cycle that contains today's date.
  Returns `nil` if no current cycle exists.

  ## Parameters

    - `member` - Member struct with loaded membership_fee_cycles and membership_fee_type
    - `today` - Optional date to use as reference (defaults to today)

  ## Returns

    - `%MembershipFeeCycle{}` if found
    - `nil` if no current cycle exists

  ## Examples

      # Member with cycles, today is 2024-06-15 (within Q2 2024)
      iex> cycle = MvWeb.Helpers.MembershipFeeHelpers.get_current_cycle(member)
      # => %MembershipFeeCycle{cycle_start: ~D[2024-04-01], ...}
  """
  @spec get_current_cycle(Member.t() | nil, Date.t() | nil) :: MembershipFeeCycle.t() | nil
  def get_current_cycle(member, today \\ nil)

  def get_current_cycle(nil, _today), do: nil

  def get_current_cycle(%Member{} = member, today) do
    today = today || Date.utc_today()

    case member.membership_fee_type do
      nil ->
        nil

      fee_type ->
        cycles = member.membership_fee_cycles || []

        cycles
        |> Enum.filter(fn cycle ->
          CalendarCycles.current_cycle?(cycle.cycle_start, fee_type.interval, today)
        end)
        |> Enum.sort_by(& &1.cycle_start, {:desc, Date})
        |> List.first()
    end
  end

  @doc """
  Gets the CSS color class for a status badge.

  ## Examples

      iex> MvWeb.Helpers.MembershipFeeHelpers.status_color(:paid)
      "badge-success"

      iex> MvWeb.Helpers.MembershipFeeHelpers.status_color(:unpaid)
      "badge-error"

      iex> MvWeb.Helpers.MembershipFeeHelpers.status_color(:suspended)
      "badge-ghost"
  """
  @spec status_color(:paid | :unpaid | :suspended) :: String.t()
  def status_color(:paid), do: "badge-success"
  def status_color(:unpaid), do: "badge-error"
  def status_color(:suspended), do: "badge-ghost"

  @doc """
  Gets the icon name for a status.

  ## Examples

      iex> MvWeb.Helpers.MembershipFeeHelpers.status_icon(:paid)
      "hero-check-circle"

      iex> MvWeb.Helpers.MembershipFeeHelpers.status_icon(:unpaid)
      "hero-x-circle"

      iex> MvWeb.Helpers.MembershipFeeHelpers.status_icon(:suspended)
      "hero-pause-circle"
  """
  @spec status_icon(:paid | :unpaid | :suspended) :: String.t()
  def status_icon(:paid), do: "hero-check-circle"
  def status_icon(:unpaid), do: "hero-x-circle"
  def status_icon(:suspended), do: "hero-pause-circle"

  # Private helper function for date formatting
  defp format_date(%Date{} = date) do
    Calendar.strftime(date, "%d.%m.%Y")
  end
end
