defmodule PlausibleWeb.Live.RegisterForm do
  @moduledoc """
  LiveView for registration form.
  """

  use PlausibleWeb, :live_view
  use Phoenix.HTML
  import PlausibleWeb.Live.Components.Form

  alias Plausible.Auth
  alias Plausible.Repo

  def mount(params, _session, socket) do
    socket =
      assign_new(socket, :invitation, fn ->
        if invitation_id = params["invitation_id"] do
          Repo.get_by(Auth.Invitation, invitation_id: invitation_id)
        end
      end)

    if socket.assigns.live_action == :register_from_invitation_form and
         socket.assigns.invitation == nil do
      {:ok, assign(socket, invitation_expired: true)}
    else
      changeset =
        if invitation = socket.assigns.invitation do
          Auth.User.settings_changeset(%Auth.User{email: invitation.email})
        else
          Auth.User.settings_changeset(%Auth.User{})
        end

      {:ok,
       assign(socket,
         form: to_form(changeset),
         captcha_error: nil,
         password_strength: Auth.User.password_strength(changeset),
         trigger_submit: false
       )}
    end
  end

  def render(%{invitation_expired: true} = assigns) do
    ~H"""
    <div class="mx-auto mt-6 text-center dark:text-gray-300">
      <h1 class="text-3xl font-black">Plausible Analytics</h1>
      <div class="text-xl font-medium">Lightweight and privacy-friendly web analytics</div>
    </div>

    <div class="w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8">
      <h2 class="text-xl font-black dark:text-gray-100">Invitation expired</h2>

      <p class="mt-4 text-sm">
        Your invitation has expired or been revoked. Please request fresh one or you can <%= link(
          "sign up",
          class: "text-indigo-600 hover:text-indigo-900",
          to: Routes.auth_path(@socket, :register)
        ) %> for a 30-day unlimited free trial without an invitation.
      </p>
    </div>
    """
  end

  def render(assigns) do
    ~H"""
    <div class="mx-auto mt-6 text-center dark:text-gray-300">
      <h1 class="text-3xl font-black">
        <%= if ce?() or @live_action == :register_from_invitation_form do %>
          Register your Plausible Analytics account
        <% else %>
          Register your 30-day free trial
        <% end %>
      </h1>
      <div class="text-xl font-medium">Set up privacy-friendly analytics with just a few clicks</div>
    </div>

    <div class="w-full max-w-3xl mt-4 mx-auto flex flex-shrink-0">
      <.form
        :let={f}
        for={@form}
        id="register-form"
        phx-hook="Metrics"
        phx-change="validate"
        phx-submit="register"
        phx-trigger-action={@trigger_submit}
        class="w-full max-w-md mx-auto bg-white dark:bg-gray-800 shadow-md rounded px-8 py-6 mb-4 mt-8"
      >
        <input name="_csrf_token" type="hidden" value={Plug.CSRFProtection.get_csrf_token()} />

        <h2 class="text-xl font-black dark:text-gray-100">Enter your details</h2>

        <%= if @invitation do %>
          <.email_input field={f[:email]} for_invitation={true} />
          <.name_input field={f[:name]} />
        <% else %>
          <.name_input field={f[:name]} />
          <.email_input field={f[:email]} for_invitation={false} />
        <% end %>

        <div class="my-4">
          <div class="flex justify-between">
            <label
              for={f[:password].name}
              class="block text-sm font-medium text-gray-700 dark:text-gray-300"
            >
              Password
            </label>
            <.password_length_hint minimum={12} field={f[:password]} />
          </div>
          <div class="mt-1">
            <.password_input_with_strength
              field={f[:password]}
              strength={@password_strength}
              phx-debounce={200}
              class="dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300"
            />
          </div>
        </div>

        <div class="my-4">
          <label
            for={f[:password_confirmation].name}
            class="block text-sm font-medium text-gray-700 dark:text-gray-300"
          >
            Password confirmation
          </label>
          <div class="mt-1">
            <.input
              type="password"
              autocomplete="new-password"
              field={f[:password_confirmation]}
              phx-debounce={200}
              class="dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300"
            />
          </div>
        </div>

        <%= if PlausibleWeb.Captcha.enabled?() do %>
          <div class="mt-4">
            <div
              phx-update="ignore"
              id="hcaptcha-placeholder"
              class="h-captcha"
              data-sitekey={PlausibleWeb.Captcha.sitekey()}
            >
            </div>
            <%= if @captcha_error do %>
              <div class="text-red-500 text-xs italic mt-3"><%= @captcha_error %></div>
            <% end %>
            <script
              phx-update="ignore"
              id="hcaptcha-script"
              src="https://hcaptcha.com/1/api.js"
              async
              defer
            >
            </script>
          </div>
        <% end %>

        <% submit_text =
          if ce?() or @invitation do
            "Create my account →"
          else
            "Start my free trial →"
          end %>
        <PlausibleWeb.Components.Generic.button id="register" type="submit" class="mt-4 w-full">
          <%= submit_text %>
        </PlausibleWeb.Components.Generic.button>

        <p class="text-center text-gray-600 dark:text-gray-500  text-xs mt-4">
          Already have an account? <%= link("Log in",
            to: "/login",
            class: "underline text-gray-800 dark:text-gray-50"
          ) %> instead.
        </p>
      </.form>
      <div :if={@live_action == :register_form} class="pt-12 pl-8 hidden md:block">
        <%= PlausibleWeb.AuthView.render("_onboarding_steps.html", current_step: 0) %>
      </div>
    </div>
    """
  end

  defp name_input(assigns) do
    ~H"""
    <div class="my-4">
      <label for={@field.name} class="block text-sm font-medium text-gray-700 dark:text-gray-300">
        Full name
      </label>
      <div class="mt-1">
        <.input
          field={@field}
          placeholder="Jane Doe"
          phx-debounce={200}
          class="dark:bg-gray-900 shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 dark:border-gray-500 rounded-md dark:text-gray-300"
        />
      </div>
    </div>
    """
  end

  defp email_input(assigns) do
    email_classes = ~w(
      dark:bg-gray-900
      shadow-sm
      focus:ring-indigo-500
      focus:border-indigo-500
      block
      w-full
      sm:text-sm
      border-gray-300
      dark:border-gray-500
      rounded-md
      dark:text-gray-300
    )

    {email_readonly, email_extra_classes} =
      if assigns[:for_invitation] do
        {[readonly: "readonly"], ["bg-gray-100"]}
      else
        {[], []}
      end

    assigns =
      assigns
      |> assign(:email_readonly, email_readonly)
      |> assign(:email_classes, email_classes ++ email_extra_classes)

    ~H"""
    <div class="my-4">
      <div class="flex justify-between">
        <label for={@field.name} class="block text-sm font-medium text-gray-700 dark:text-gray-300">
          Email
        </label>
        <p class="text-xs text-gray-500 mt-1">No spam, guaranteed.</p>
      </div>
      <div class="mt-1">
        <.input
          type="email"
          field={@field}
          placeholder="example@email.com"
          phx-debounce={200}
          class={@email_classes}
          {@email_readonly}
        />
      </div>
    </div>
    """
  end

  def handle_event("validate", %{"user" => params}, socket) do
    changeset =
      params
      |> Auth.User.new()
      |> Map.put(:action, :validate)

    password_strength = Auth.User.password_strength(changeset)

    {:noreply, assign(socket, form: to_form(changeset), password_strength: password_strength)}
  end

  def handle_event(
        "register",
        %{"user" => _} = params,
        %{assigns: %{invitation: %{} = invitation}} = socket
      ) do
    if not PlausibleWeb.Captcha.enabled?() or
         PlausibleWeb.Captcha.verify(params["h-captcha-response"]) do
      user =
        params["user"]
        |> Map.put("email", invitation.email)
        |> Auth.User.new()

      user =
        case invitation.role do
          :owner -> user
          _ -> Plausible.Auth.User.remove_trial_expiry(user)
        end

      add_user(socket, user)
    else
      {:noreply, assign(socket, :captcha_error, "Please complete the captcha to register")}
    end
  end

  def handle_event("register", %{"user" => _} = params, socket) do
    if not PlausibleWeb.Captcha.enabled?() or
         PlausibleWeb.Captcha.verify(params["h-captcha-response"]) do
      user = Auth.User.new(params["user"])

      add_user(socket, user)
    else
      {:noreply, assign(socket, :captcha_error, "Please complete the captcha to register")}
    end
  end

  def handle_event("send-metrics-after", _params, socket) do
    {:noreply, assign(socket, trigger_submit: true)}
  end

  defp add_user(socket, user) do
    case Repo.insert(user) do
      {:ok, _user} ->
        on_ee do
          metrics_params =
            if socket.assigns.invitation do
              %{
                event_name: "Signup via invitation",
                params: %{
                  url:
                    Path.join(PlausibleWeb.Endpoint.url(), "/register/invitation/:invitation_id")
                }
              }
            else
              %{event_name: "Signup", params: %{}}
            end

          {:noreply, push_event(socket, "send-metrics", metrics_params)}
        else
          {:noreply, socket}
        end

      {:error, changeset} ->
        {:noreply,
         assign(socket,
           form: to_form(Map.put(changeset, :action, :validate))
         )}
    end
  end
end
