defmodule Towerops.Accounts.UserAgentParser do
  @moduledoc """
  Simple regex-based User-Agent parser for extracting browser, OS, and device information.

  This module provides basic device identification without external dependencies.
  It covers ~95% of common user agents but may not detect obscure browsers or devices.

  ## Examples

      iex> Towerops.Accounts.UserAgentParser.parse("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
      %{
        browser_name: "Chrome",
        browser_version: "120.0",
        os_name: "macOS",
        os_version: "10.15.7",
        device_type: "desktop",
        device_name: "Chrome on macOS"
      }

      iex> Towerops.Accounts.UserAgentParser.parse("Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1")
      %{
        browser_name: "Safari",
        browser_version: "17.2",
        os_name: "iOS",
        os_version: "17.2",
        device_type: "mobile",
        device_name: "Safari on iOS"
      }
  """

  @doc """
  Parses a User-Agent string and returns device metadata.

  Returns a map with the following fields:
  - `:browser_name` - Browser name (Chrome, Safari, Firefox, Edge, Opera, or "Unknown")
  - `:browser_version` - Browser version string (or nil)
  - `:os_name` - Operating system name (macOS, Windows, Linux, iOS, Android, or "Unknown")
  - `:os_version` - OS version string (or nil)
  - `:device_type` - "desktop", "mobile", or "tablet"
  - `:device_name` - Formatted string like "Chrome on macOS"

  If the User-Agent is nil or cannot be parsed, returns a map with "Unknown" values.
  """
  def parse(nil), do: unknown_device()
  def parse(""), do: unknown_device()

  def parse(user_agent) when is_binary(user_agent) do
    browser = detect_browser(user_agent)
    os = detect_os(user_agent)
    device_type = detect_device_type(user_agent, os)

    %{
      browser_name: browser.name,
      browser_version: browser.version,
      os_name: os.name,
      os_version: os.version,
      device_type: device_type,
      device_name: format_device_name(browser.name, os.name)
    }
  end

  # Browser detection

  defp detect_browser(ua) do
    cond do
      # Opera must be checked before Chrome (Opera includes Chrome in UA)
      Regex.match?(~r/OPR\/(\d+\.\d+)/, ua) || Regex.match?(~r/Opera\/(\d+\.\d+)/, ua) ->
        version = extract_version(ua, ~r/(?:OPR|Opera)\/(\d+\.\d+)/)
        %{name: "Opera", version: version}

      # Edge must be checked before Chrome (Edge includes Chrome in UA)
      Regex.match?(~r/Edg(e|A|iOS)?\/(\d+\.\d+)/, ua) ->
        version = extract_version(ua, ~r/Edg(?:e|A|iOS)?\/(\d+\.\d+)/)
        %{name: "Edge", version: version}

      # Chrome must be checked before Safari (Chrome includes Safari in UA)
      Regex.match?(~r/Chrome\/(\d+\.\d+)/, ua) ->
        version = extract_version(ua, ~r/Chrome\/(\d+\.\d+)/)
        %{name: "Chrome", version: version}

      # Firefox
      Regex.match?(~r/Firefox\/(\d+\.\d+)/, ua) ->
        version = extract_version(ua, ~r/Firefox\/(\d+\.\d+)/)
        %{name: "Firefox", version: version}

      # Safari (check last because many browsers include Safari in UA)
      Regex.match?(~r/Safari\//, ua) && Regex.match?(~r/Version\/(\d+\.\d+)/, ua) ->
        version = extract_version(ua, ~r/Version\/(\d+\.\d+)/)
        %{name: "Safari", version: version}

      true ->
        %{name: "Unknown", version: nil}
    end
  end

  # OS detection

  defp detect_os(ua) do
    cond do
      # iOS (must check before macOS because iOS UA contains "Mac OS X")
      Regex.match?(~r/iPhone|iPad|iPod/, ua) ->
        version = extract_version(ua, ~r/OS (\d+[_\d]*)/)
        %{name: "iOS", version: format_ios_version(version)}

      # macOS
      Regex.match?(~r/Macintosh|Mac OS X/, ua) ->
        version = extract_version(ua, ~r/Mac OS X (\d+[_\d]*)/)
        %{name: "macOS", version: format_mac_version(version)}

      # Android
      Regex.match?(~r/Android/, ua) ->
        version = extract_version(ua, ~r/Android (\d+\.\d+)/)
        %{name: "Android", version: version}

      # Windows
      Regex.match?(~r/Windows/, ua) ->
        version = detect_windows_version(ua)
        %{name: "Windows", version: version}

      # Linux
      Regex.match?(~r/Linux/, ua) ->
        %{name: "Linux", version: nil}

      true ->
        %{name: "Unknown", version: nil}
    end
  end

  # Device type detection

  defp detect_device_type(ua, os) do
    cond do
      # Tablets
      Regex.match?(~r/iPad/, ua) || Regex.match?(~r/Android.*Tablet/, ua) ->
        "tablet"

      # Mobile devices
      Regex.match?(~r/iPhone|iPod|Android.*Mobile|Mobile/, ua) || os.name == "iOS" ->
        "mobile"

      # Default to desktop
      true ->
        "desktop"
    end
  end

  # Helper functions

  defp extract_version(ua, regex) do
    case Regex.run(regex, ua) do
      [_, version] -> version
      _ -> nil
    end
  end

  defp format_ios_version(nil), do: nil

  defp format_ios_version(version) do
    # Convert iOS version from "17_2_1" to "17.2.1"
    String.replace(version, "_", ".")
  end

  defp format_mac_version(nil), do: nil

  defp format_mac_version(version) do
    # Convert macOS version from "10_15_7" to "10.15.7"
    String.replace(version, "_", ".")
  end

  defp detect_windows_version(ua) do
    cond do
      Regex.match?(~r/Windows NT 10\.0/, ua) -> "10"
      Regex.match?(~r/Windows NT 6\.3/, ua) -> "8.1"
      Regex.match?(~r/Windows NT 6\.2/, ua) -> "8"
      Regex.match?(~r/Windows NT 6\.1/, ua) -> "7"
      Regex.match?(~r/Windows NT/, ua) -> extract_version(ua, ~r/Windows NT (\d+\.\d+)/)
      true -> nil
    end
  end

  defp format_device_name(browser, os) do
    "#{browser} on #{os}"
  end

  defp unknown_device do
    %{
      browser_name: "Unknown",
      browser_version: nil,
      os_name: "Unknown",
      os_version: nil,
      device_type: "desktop",
      device_name: "Unknown on Unknown"
    }
  end
end
