defmodule Towerops.Accounts.BrowserSession do
  @moduledoc """
  Schema for tracking active browser sessions.

  This schema is used for session management and GDPR compliance:
  - Each browser session is linked to a user_token (one-to-one relationship)
  - Device and browser metadata parsed from User-Agent header
  - Location data enriched via GeoIP lookup
  - Activity tracking via last_activity_at timestamp
  - On user deletion, user_id is set to NULL (anonymization) but metadata preserved

  ## Lifecycle
  - Created when user logs in (linked to user_token)
  - Updated on each request via UpdateSessionActivity plug
  - Deleted when user logs out or revokes session
  - Automatically cleaned up after expires_at timestamp
  """

  use Ecto.Schema

  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id

  schema "browser_sessions" do
    # Device/browser metadata
    field :device_name, :string
    field :browser_name, :string
    field :browser_version, :string
    field :os_name, :string
    field :os_version, :string
    field :device_type, :string

    # Network/location data
    field :ip_address, :string
    field :country_code, :string
    field :country_name, :string
    field :city_name, :string
    field :subdivision_1_name, :string

    # Activity tracking
    field :last_activity_at, :utc_datetime
    field :expires_at, :utc_datetime

    # GDPR tracking
    field :anonymized_at, :utc_datetime

    belongs_to :user, Towerops.Accounts.User
    belongs_to :user_token, Towerops.Accounts.UserToken

    timestamps(type: :utc_datetime)
  end

  @doc """
  Creates a changeset for a new browser session.

  ## Required fields
  - `:user_id` - User who owns the session
  - `:user_token_id` - Associated authentication token
  - `:ip_address` - IP address of the session
  - `:last_activity_at` - Initial activity timestamp
  - `:expires_at` - Session expiration timestamp

  ## Optional fields
  - `:device_name` - Formatted device name (e.g., "Chrome on macOS")
  - `:browser_name`, `:browser_version` - Parsed from User-Agent
  - `:os_name`, `:os_version` - Parsed from User-Agent
  - `:device_type` - desktop, mobile, or tablet
  - `:country_code`, `:country_name`, `:city_name`, `:subdivision_1_name` - From GeoIP lookup

  ## Examples

      iex> changeset(%BrowserSession{}, %{
      ...>   user_id: "123",
      ...>   user_token_id: "456",
      ...>   ip_address: "192.168.1.1",
      ...>   device_name: "Chrome on macOS",
      ...>   browser_name: "Chrome",
      ...>   os_name: "macOS",
      ...>   device_type: "desktop",
      ...>   last_activity_at: ~U[2026-01-29 19:00:00Z],
      ...>   expires_at: ~U[2026-02-12 19:00:00Z],
      ...>   country_name: "United States",
      ...>   city_name: "San Francisco"
      ...> })
  """
  def create_changeset(browser_session, attrs) do
    browser_session
    |> cast(attrs, [
      :user_id,
      :user_token_id,
      :device_name,
      :browser_name,
      :browser_version,
      :os_name,
      :os_version,
      :device_type,
      :ip_address,
      :country_code,
      :country_name,
      :city_name,
      :subdivision_1_name,
      :last_activity_at,
      :expires_at
    ])
    |> validate_required([:user_id, :user_token_id, :ip_address, :last_activity_at, :expires_at])
    |> validate_length(:ip_address, max: 45)
    |> validate_length(:device_name, max: 255)
    |> validate_length(:browser_name, max: 100)
    |> validate_length(:os_name, max: 100)
    |> validate_inclusion(:device_type, ["desktop", "mobile", "tablet"], allow_nil: true)
    |> foreign_key_constraint(:user_id)
    |> foreign_key_constraint(:user_token_id)
    |> unique_constraint(:user_token_id)
  end

  @doc """
  Creates a changeset for updating the last_activity_at timestamp.

  This is called by the UpdateSessionActivity plug on each request
  to keep track of when the session was last used.
  """
  def touch_changeset(browser_session) do
    change(browser_session, %{last_activity_at: DateTime.utc_now()})
  end

  @doc """
  Anonymizes a browser session by setting user_id to nil and recording timestamp.

  This is used for GDPR compliance when a user is deleted. The IP address,
  location data, and device metadata are preserved for security analysis.
  """
  def anonymize_changeset(browser_session) do
    change(browser_session, %{user_id: nil, anonymized_at: DateTime.utc_now()})
  end
end
