defmodule MvWeb.Router do
  use MvWeb, :router

  use AshAuthentication.Phoenix.Router

  import AshAuthentication.Plug.Helpers

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {MvWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug :load_from_session
    plug :set_locale
    plug MvWeb.Plugs.CheckPagePermission
  end

  pipeline :api do
    plug :accepts, ["json"]
    plug :load_from_bearer
    plug :set_actor, :user
  end

  scope "/", MvWeb do
    pipe_through :browser

    ash_authentication_live_session :authenticated_routes do
      # in each liveview, add one of the following at the top of the module:
      #
      # If an authenticated user must be present:
      # on_mount {MvWeb.LiveUserAuth, :live_user_required}
      #
      # If an authenticated user *may* be present:
      # on_mount {MvWeb.LiveUserAuth, :live_user_optional}
      #
      # If an authenticated user must *not* be present:
      # on_mount {MvWeb.LiveUserAuth, :live_no_user}
    end
  end

  scope "/", MvWeb do
    pipe_through :browser

    @doc """
    AshAuthentication-specific: We define that all routes can only be accessed when the user is signed in.
    """
    ash_authentication_live_session :authentication_required,
      on_mount: [
        {MvWeb.LiveUserAuth, :live_user_required},
        {MvWeb.LiveHelpers, :ensure_user_role_loaded},
        {MvWeb.LiveHelpers, :check_page_permission_on_params}
      ] do
      live "/", MemberLive.Index, :index

      live "/members", MemberLive.Index, :index
      live "/members/new", MemberLive.Form, :new
      live "/members/:id/edit", MemberLive.Form, :edit
      live "/members/:id", MemberLive.Show, :show
      live "/members/:id/show/edit", MemberLive.Show, :edit

      live "/users", UserLive.Index, :index
      live "/users/new", UserLive.Form, :new
      live "/users/:id/edit", UserLive.Form, :edit
      live "/users/:id", UserLive.Show, :show
      live "/users/:id/show/edit", UserLive.Show, :edit

      live "/settings", GlobalSettingsLive

      # Membership Fee Settings
      live "/membership_fee_settings", MembershipFeeSettingsLive

      # Membership Fee Types Management
      live "/membership_fee_types", MembershipFeeTypeLive.Index, :index

      # Statistics
      live "/statistics", StatisticsLive, :index
      live "/membership_fee_types/new", MembershipFeeTypeLive.Form, :new
      live "/membership_fee_types/:id/edit", MembershipFeeTypeLive.Form, :edit

      # Groups Management
      live "/groups", GroupLive.Index, :index
      live "/groups/new", GroupLive.Form, :new
      live "/groups/:slug", GroupLive.Show, :show
      live "/groups/:slug/edit", GroupLive.Form, :edit

      # Role Management (Admin only)
      live "/admin/roles", RoleLive.Index, :index
      live "/admin/roles/new", RoleLive.Form, :new
      live "/admin/roles/:id", RoleLive.Show, :show
      live "/admin/roles/:id/edit", RoleLive.Form, :edit

      # Import (Admin only)
      live "/admin/import", ImportLive

      post "/members/export.csv", MemberExportController, :export
      post "/members/export.pdf", MemberPdfExportController, :export
      post "/set_locale", LocaleController, :set_locale
    end

    # OIDC account linking - user needs to verify password (MUST be before auth_routes!)
    live "/auth/link-oidc-account", LinkOidcAccountLive

    # ASHAUTHENTICATION GENERATED AUTH ROUTES
    auth_routes AuthController, Mv.Accounts.User, path: "/auth"
    sign_out_route AuthController

    # Remove these if you'd like to use your own authentication views
    sign_in_route register_path: "/register",
                  reset_path: "/reset",
                  auth_routes_prefix: "/auth",
                  on_mount: [{MvWeb.LiveUserAuth, :live_no_user}],
                  overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.DaisyUI],
                  gettext_backend: {MvWeb.Gettext, "auth"}

    # Remove this if you do not want to use the reset password feature
    reset_route auth_routes_prefix: "/auth",
                overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.DaisyUI],
                gettext_backend: {MvWeb.Gettext, "auth"}

    # Remove this if you do not use the confirmation strategy
    confirm_route Mv.Accounts.User, :confirm_new_user,
      auth_routes_prefix: "/auth",
      overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.DaisyUI],
      gettext_backend: {MvWeb.Gettext, "auth"}

    # Remove this if you do not use the magic link strategy.
    # magic_sign_in_route(Mv.Accounts.User, :magic_link,
    #   auth_routes_prefix: "/auth",
    #   overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default]
    # )
  end

  # Other scopes may use custom stacks.
  # scope "/api", MvWeb do
  #   pipe_through :api
  # end

  # Enable LiveDashboard and Swoosh mailbox preview in development
  if Application.compile_env(:mv, :dev_routes) do
    # If you want to use the LiveDashboard in production, you should put
    # it behind authentication and allow only admins to access it.
    # If your application does not have an admins-only section yet,
    # you can use Plug.BasicAuth to set up some basic authentication
    # as long as you are also using SSL (which you should anyway).
    import Phoenix.LiveDashboard.Router

    scope "/dev" do
      pipe_through :browser

      live_dashboard "/dashboard", metrics: MvWeb.Telemetry
      forward "/mailbox", Plug.Swoosh.MailboxPreview
    end
  end

  if Application.compile_env(:mv, :dev_routes) do
    import AshAdmin.Router

    scope "/admin" do
      pipe_through :browser

      ash_admin "/"
    end
  end

  defp set_locale(conn, _opts) do
    locale =
      get_session(conn, :locale) ||
        get_locale_from_cookie(conn) ||
        extract_locale_from_headers(conn.req_headers)

    Gettext.put_locale(MvWeb.Gettext, locale)

    conn
    |> put_session(:locale, locale)
    |> assign(:locale, locale)
  end

  defp get_locale_from_cookie(conn) do
    case conn.req_cookies do
      %{"locale" => locale} when locale in ["en", "de"] -> locale
      _ -> nil
    end
  end

  # Get locale from user
  defp extract_locale_from_headers(headers) do
    headers
    |> Enum.find_value(fn
      {"accept-language", value} -> value
      _ -> nil
    end)
    |> parse_accept_language()
    |> Enum.find(&supported_locale?/1)
    |> fallback_locale()
  end

  defp parse_accept_language(nil), do: []

  defp parse_accept_language(header) do
    header
    |> String.split(",")
    |> Enum.map(&String.trim/1)
    |> Enum.map(fn lang ->
      lang
      # we only want the first part
      |> String.split(";")
      |> hd()
      |> String.split("-")
      |> hd()
    end)
  end

  # Our supported languages for now are german and english, english as fallback language
  defp supported_locale?(locale), do: locale in ["en", "de"]
  defp fallback_locale(nil), do: "en"
  defp fallback_locale(locale), do: locale
end
