defmodule ToweropsWeb.Api.MobileAuthController do
  @moduledoc """
  API controller for mobile app authentication.

  Handles QR code-based authentication flow:
  1. Web app creates QR token (handled by LiveView)
  2. Mobile app scans QR code and calls verify_qr_token
  3. Mobile app calls complete_qr_login to get session token
  """
  use ToweropsWeb, :controller

  alias Towerops.MobileSessions

  @doc """
  POST /api/v1/mobile/auth/qr/verify

  Verifies a QR login token is valid (not expired, not used).

  Request body:
  {
    "token": "base64-encoded-token"
  }

  Response:
  {
    "valid": true,
    "user_email": "user@example.com"
  }
  """
  def verify_qr_token(conn, %{"token" => token}) do
    case MobileSessions.get_qr_login_token(token) do
      nil ->
        conn
        |> put_status(:unauthorized)
        |> json(%{valid: false, error: "Invalid or expired token"})

      qr_token ->
        # Preload user to return email
        qr_token = Towerops.Repo.preload(qr_token, :user)

        json(conn, %{
          valid: true,
          user_email: qr_token.user.email
        })
    end
  end

  def verify_qr_token(conn, _params) do
    conn
    |> put_status(:bad_request)
    |> json(%{error: "Missing token parameter"})
  end

  @doc """
  POST /api/v1/mobile/auth/qr/complete

  Completes QR login by creating a mobile session.

  Request body:
  {
    "token": "base64-encoded-qr-token",
    "device_name": "iPhone 15 Pro",
    "device_os": "iOS 17.2",
    "app_version": "1.0.0"
  }

  Response:
  {
    "session_token": "long-lived-session-token",
    "expires_at": "2026-04-15T19:44:25Z",
    "user": {
      "id": "uuid",
      "email": "user@example.com"
    }
  }
  """
  def complete_qr_login(conn, params) do
    token = params["token"]
    device_name = params["device_name"]
    device_os = params["device_os"]
    app_version = params["app_version"]
    push_token = params["push_token"]
    push_platform = params["push_platform"]

    if token do
      device_attrs = %{
        device_name: device_name,
        device_os: device_os,
        app_version: app_version,
        push_token: push_token,
        push_platform: push_platform
      }

      case MobileSessions.complete_qr_login(token, device_attrs) do
        {:ok, session} ->
          session = Towerops.Repo.preload(session, :user)

          json(conn, %{
            session_token: session.token,
            expires_at: session.expires_at,
            user: %{
              id: session.user.id,
              email: session.user.email
            }
          })

        {:error, :invalid_token} ->
          conn
          |> put_status(:unauthorized)
          |> json(%{error: "Invalid or expired token"})

        {:error, changeset} ->
          conn
          |> put_status(:unprocessable_entity)
          |> json(%{error: "Failed to create session", details: translate_errors(changeset)})
      end
    else
      conn
      |> put_status(:bad_request)
      |> json(%{error: "Missing token parameter"})
    end
  end

  @doc """
  GET /api/v1/mobile/auth/session

  Returns current session info (requires mobile auth).

  Response:
  {
    "id": "session-uuid",
    "device_name": "iPhone 15 Pro",
    "device_os": "iOS 17.2",
    "app_version": "1.0.0",
    "last_used_at": "2026-01-15T19:44:25Z",
    "expires_at": "2026-04-15T19:44:25Z"
  }
  """
  def get_session(conn, _params) do
    session = conn.assigns.current_mobile_session

    json(conn, %{
      id: session.id,
      device_name: session.device_name,
      device_os: session.device_os,
      app_version: session.app_version,
      last_used_at: session.last_used_at,
      expires_at: session.expires_at
    })
  end

  @doc """
  DELETE /api/v1/mobile/auth/session

  Revokes the current mobile session (logout).

  Response:
  {
    "success": true
  }
  """
  def revoke_session(conn, _params) do
    session = conn.assigns.current_mobile_session

    case MobileSessions.revoke_session(session.id) do
      {:ok, _} ->
        json(conn, %{success: true})

      {:error, _} ->
        conn
        |> put_status(:internal_server_error)
        |> json(%{error: "Failed to revoke session"})
    end
  end

  # Helper to translate changeset errors
  defp translate_errors(changeset) do
    Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
      Regex.replace(~r"%{(\w+)}", msg, fn _, key ->
        safe_translate_key(key, opts)
      end)
    end)
  end

  # Safely translate error message keys to prevent atom exhaustion
  defp safe_translate_key(key, opts) do
    # Validate key length and format before converting to atom to prevent abuse
    if String.length(key) <= 50 and String.match?(key, ~r/^[a-z_]+$/) do
      opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
    else
      key
    end
  end
end
