defmodule BerrypodWeb.Admin.Newsletter.CampaignForm do
  use BerrypodWeb, :live_view

  alias Berrypod.Newsletter

  @impl true
  def mount(_params, _session, socket) do
    {:ok,
     socket
     |> assign(:page_title, "New campaign")
     |> assign(:campaign, nil)
     |> assign(:subscriber_count, Newsletter.confirmed_subscriber_count())
     |> assign(:form, to_form(%{"subject" => "", "body" => ""}, as: :campaign))
     |> assign(:save_status, :idle)}
  end

  @impl true
  def handle_params(%{"id" => id}, _uri, socket) do
    campaign = Newsletter.get_campaign!(id)

    title =
      if campaign.status == "draft",
        do: "Edit campaign",
        else: "Campaign: #{campaign.subject}"

    {:noreply,
     socket
     |> assign(:page_title, title)
     |> assign(:campaign, campaign)
     |> assign(
       :form,
       to_form(
         %{
           "subject" => campaign.subject,
           "body" => campaign.body
         },
         as: :campaign
       )
     )}
  end

  def handle_params(_params, _uri, socket), do: {:noreply, socket}

  @impl true
  def handle_event("validate", %{"campaign" => params}, socket) do
    {:noreply, assign(socket, form: to_form(params, as: :campaign), save_status: :idle)}
  end

  def handle_event("save_draft", %{"campaign" => params}, socket) do
    case save_campaign(socket.assigns.campaign, params) do
      {:ok, campaign} ->
        {:noreply,
         socket
         |> assign(:campaign, campaign)
         |> assign(:save_status, :saved)}

      {:error, _changeset} ->
        {:noreply, assign(socket, :save_status, :error)}
    end
  end

  def handle_event("send_now", _params, socket) do
    params = current_form_params(socket)

    with {:ok, campaign} <- save_campaign(socket.assigns.campaign, params),
         {:ok, campaign} <- Newsletter.send_campaign_now(campaign) do
      {:noreply,
       socket
       |> assign(:campaign, campaign)
       |> put_flash(:info, "Campaign is being sent!")
       |> push_navigate(to: ~p"/admin/newsletter?tab=campaigns")}
    else
      {:error, _} ->
        {:noreply, put_flash(socket, :error, "Failed to send campaign")}
    end
  end

  def handle_event("send_test", _params, socket) do
    params = current_form_params(socket)
    to_email = socket.assigns.current_scope.user.email

    if params["subject"] == "" || params["body"] == "" do
      {:noreply, put_flash(socket, :error, "Fill in subject and body first")}
    else
      # Build a temporary campaign struct for the notifier
      campaign = %Newsletter.Campaign{
        subject: params["subject"],
        body: params["body"]
      }

      case Newsletter.Notifier.deliver_test(campaign, to_email) do
        {:ok, _} ->
          {:noreply, put_flash(socket, :info, "Test email sent to #{to_email}")}

        {:error, _} ->
          {:noreply, put_flash(socket, :error, "Failed to send test email")}
      end
    end
  end

  def handle_event("schedule", _params, socket) do
    params = current_form_params(socket)
    scheduled_at = parse_schedule_time(params["scheduled_at"])

    with {:ok, campaign} <- save_campaign(socket.assigns.campaign, params),
         {:ok, _campaign} <- Newsletter.schedule_campaign(campaign, scheduled_at) do
      {:noreply,
       socket
       |> put_flash(:info, "Campaign scheduled")
       |> push_navigate(to: ~p"/admin/newsletter?tab=campaigns")}
    else
      {:error, _} ->
        {:noreply, put_flash(socket, :error, "Failed to schedule campaign")}
    end
  end

  defp save_campaign(nil, params) do
    Newsletter.create_campaign(%{
      subject: params["subject"],
      body: params["body"]
    })
  end

  defp save_campaign(%{status: "draft"} = campaign, params) do
    Newsletter.update_campaign(campaign, %{
      subject: params["subject"],
      body: params["body"]
    })
  end

  defp save_campaign(campaign, _params), do: {:ok, campaign}

  defp parse_schedule_time(nil), do: DateTime.utc_now() |> DateTime.add(3600)
  defp parse_schedule_time(""), do: DateTime.utc_now() |> DateTime.add(3600)

  defp parse_schedule_time(str) do
    case DateTime.from_iso8601(str <> ":00Z") do
      {:ok, dt, _} -> dt
      _ -> DateTime.utc_now() |> DateTime.add(3600)
    end
  end

  @impl true
  def render(assigns) do
    ~H"""
    <.header>
      {@page_title}
      <:subtitle>
        <%= cond do %>
          <% readonly?(@campaign) -> %>
            This campaign was sent on {format_date(@campaign.sent_at)} to {@campaign.sent_count} subscribers
          <% @subscriber_count > 0 -> %>
            {@subscriber_count} confirmed subscribers will receive this email
          <% true -> %>
            No confirmed subscribers yet
        <% end %>
      </:subtitle>
    </.header>

    <div class="admin-content-medium admin-section">
      <.form for={@form} phx-change="validate" phx-submit="save_draft">
        <div class="admin-stack">
          <.input
            field={@form[:subject]}
            type="text"
            label="Subject"
            required
            disabled={readonly?(@campaign)}
            placeholder="Your campaign subject"
          />

          <.input
            field={@form[:body]}
            type="textarea"
            label="Body (plain text)"
            required
            disabled={readonly?(@campaign)}
            rows="12"
            placeholder="Hello!\n\nYour newsletter content here.\n\nUnsubscribe: {{unsubscribe_url}}"
          />

          <p
            :if={!readonly?(@campaign)}
            class="admin-help-text"
          >
            Use <code>{"{{unsubscribe_url}}"}</code>
            to insert the unsubscribe link. This is required for GDPR compliance.
          </p>

          <p
            :if={missing_unsubscribe_url?(@form[:body].value) && !readonly?(@campaign)}
            class="admin-warning-text"
          >
            <.icon name="hero-exclamation-triangle" class="size-4 shrink-0" /> Body is missing
            <code>{"{{unsubscribe_url}}"}</code>
            — this is required for GDPR compliance.
          </p>

          <%= if @form[:body].value && @form[:body].value != "" do %>
            <details>
              <summary class="admin-preview-summary">
                Preview
              </summary>
              <pre class="admin-preview-body">{preview_body(@form[:body].value)}</pre>
            </details>
          <% end %>

          <div
            :if={!readonly?(@campaign)}
            class="admin-campaign-actions"
          >
            <.button type="submit">
              Save draft
            </.button>
            <.inline_feedback status={@save_status} />

            <button
              type="button"
              phx-click="send_test"
              class="admin-btn admin-btn-ghost"
            >
              <.icon name="hero-envelope" class="size-4" /> Send test
            </button>

            <button
              type="button"
              phx-click="send_now"
              data-confirm={"Send this campaign to #{@subscriber_count} subscribers now?"}
              class="admin-btn admin-btn-primary admin-btn-success"
              disabled={@subscriber_count == 0}
            >
              <.icon name="hero-paper-airplane" class="size-4" /> Send now
            </button>

            <.link
              navigate={~p"/admin/newsletter?tab=campaigns"}
              class="admin-btn admin-btn-ghost"
            >
              Cancel
            </.link>
          </div>

          <div :if={readonly?(@campaign)} class="admin-readonly-actions">
            <.link navigate={~p"/admin/newsletter?tab=campaigns"} class="admin-btn admin-btn-ghost">
              <.icon name="hero-arrow-left" class="size-4" /> Back to campaigns
            </.link>
          </div>
        </div>
      </.form>
    </div>
    """
  end

  defp current_form_params(socket) do
    form = socket.assigns.form
    %{"subject" => form[:subject].value || "", "body" => form[:body].value || ""}
  end

  defp readonly?(%{status: status}) when status not in ["draft"], do: true
  defp readonly?(_), do: false

  defp missing_unsubscribe_url?(nil), do: false
  defp missing_unsubscribe_url?(""), do: false
  defp missing_unsubscribe_url?(body), do: not String.contains?(body, "{{unsubscribe_url}}")

  defp format_date(nil), do: "—"
  defp format_date(datetime), do: Calendar.strftime(datetime, "%-d %b %Y")

  defp preview_body(body) do
    sample_url = BerrypodWeb.Endpoint.url() <> "/unsubscribe/sample-token"
    String.replace(body, "{{unsubscribe_url}}", sample_url)
  end
end
