defmodule Algora.Events do
  import Ecto.Query

  alias Algora.{Repo}
  alias Algora.Library.Video
  alias Algora.Events.Event
  alias Algora.Accounts.{User, Identity}

  def unsubscribe(user, channel_id) do
    %Event{
      actor_id: "user_#{user.id}",
      user_id: user.id,
      channel_id: channel_id,
      name: :unsubscribed
    }
    |> Event.changeset(%{})
    |> Repo.insert()
  end

  def toggle_subscription_event(user, show) do
    name = if subscribed?(user, show), do: :unsubscribed, else: :subscribed

    %Event{
      actor_id: "user_#{user.id}",
      user_id: user.id,
      show_id: show.id,
      channel_id: show.user_id,
      name: name
    }
    |> Event.changeset(%{})
    |> Repo.insert()
  end

  def toggle_rsvp_event(user, show) do
    name = if rsvpd?(user, show), do: :unrsvpd, else: :rsvpd

    %Event{
      actor_id: "user_#{user.id}",
      user_id: user.id,
      show_id: show.id,
      channel_id: show.user_id,
      name: name
    }
    |> Event.changeset(%{})
    |> Repo.insert()
  end

  def subscribed?(nil, _show), do: false

  def subscribed?(user, show) do
    event =
      from(
        e in Event,
        where:
          e.channel_id == ^show.user_id and
            e.user_id == ^user.id and
            (e.name == :subscribed or
               e.name == :unsubscribed),
        order_by: [desc: e.inserted_at],
        limit: 1
      )
      |> Repo.one()

    event && event.name == :subscribed
  end

  def rsvpd?(nil, _show), do: false

  def rsvpd?(user, show) do
    event =
      from(
        e in Event,
        where:
          e.channel_id == ^show.user_id and
            e.user_id == ^user.id and
            (e.name == :rsvpd or
               e.name == :unrsvpd),
        order_by: [desc: e.inserted_at],
        limit: 1
      )
      |> Repo.one()

    event && event.name == :rsvpd
  end

  def fetch_attendees(show) do
    # Get the latest relevant events (:rsvpd and :unrsvpd) for each user
    latest_events_query =
      from(e in Event,
        where: e.channel_id == ^show.user_id and e.name in [:rsvpd, :unrsvpd],
        order_by: [desc: e.inserted_at],
        distinct: e.user_id
      )

    # Join user data and filter for :rsvpd events
    from(e in subquery(latest_events_query),
      join: u in User,
      on: e.user_id == u.id,
      join: i in Identity,
      on: i.user_id == u.id and i.provider == "github",
      select_merge: %{
        user_handle: u.handle,
        user_display_name: coalesce(u.name, u.handle),
        user_email: u.email,
        user_avatar_url: u.avatar_url,
        user_github_handle: i.provider_login
      },
      where: e.name == :rsvpd,
      order_by: [desc: e.inserted_at, desc: e.id]
    )
    |> Repo.all()
  end

  def fetch_unique_viewers(user) do
    subquery_first_watched =
      from(e in Event,
        where: e.channel_id == ^user.id and e.name in [:watched, :subscribed],
        order_by: [asc: e.inserted_at],
        distinct: e.user_id
      )

    from(e in subquery(subquery_first_watched),
      join: u in User,
      on: e.user_id == u.id,
      join: i in Identity,
      on: i.user_id == u.id and i.provider == "github",
      left_join: v in Video,
      on: e.video_id == v.id,
      select_merge: %{
        user_handle: u.handle,
        user_display_name: coalesce(u.name, u.handle),
        user_email: u.email,
        user_avatar_url: u.avatar_url,
        user_github_handle: i.provider_login,
        first_video_id: e.video_id,
        first_video_title: v.title
      },
      distinct: e.user_id,
      order_by: [desc: e.inserted_at, desc: e.id]
    )
    |> Repo.all()
  end

  def fetch_unique_subscribers(user) do
    # Get the latest relevant events (:subscribed and :unsubscribed) for each user
    latest_events_query =
      from(e in Event,
        where: e.channel_id == ^user.id and e.name in [:subscribed, :unsubscribed],
        order_by: [desc: e.inserted_at],
        distinct: e.user_id
      )

    # Join user data and filter for :subscribed events
    from(e in subquery(latest_events_query),
      join: u in User,
      on: e.user_id == u.id,
      join: i in Identity,
      on: i.user_id == u.id and i.provider == "github",
      left_join: v in Video,
      on: e.video_id == v.id,
      select_merge: %{
        user_handle: u.handle,
        user_display_name: coalesce(u.name, u.handle),
        user_email: u.email,
        user_avatar_url: u.avatar_url,
        user_github_handle: i.provider_login,
        first_video_id: e.video_id,
        first_video_title: v.title
      },
      where: e.name == :subscribed,
      order_by: [desc: e.inserted_at, desc: e.id]
    )
    |> Repo.all()
  end

  def fetch_subscriptions(user) do
    # Get the latest relevant events (:subscribed and :unsubscribed) for each user
    latest_events_query =
      from(e in Event,
        where: e.user_id == ^user.id and e.name in [:subscribed, :unsubscribed],
        order_by: [desc: e.inserted_at],
        distinct: e.channel_id
      )

    # Join user data and filter for :subscribed events
    from(e in subquery(latest_events_query),
      join: u in User,
      on: e.channel_id == u.id,
      join: i in Identity,
      on: i.user_id == u.id and i.provider == "github",
      select_merge: %{
        user_handle: u.handle,
        user_display_name: coalesce(u.name, u.handle),
        user_avatar_url: u.avatar_url,
        user_meta: i.provider_meta
      },
      where: e.name == :subscribed,
      order_by: [desc: e.inserted_at, desc: e.id]
    )
    |> Repo.all()
  end

  def log_watched(user, video) do
    actor_id = if user, do: "user_#{user.id}", else: "guest_#{hash_actor_id()}"

    %Event{
      actor_id: actor_id,
      user_id: user && user.id,
      video_id: video.id,
      channel_id: video.user_id,
      name: :watched
    }
    |> Event.changeset(%{})
    |> Repo.insert()
  end

  # TODO:
  defp hash_actor_id, do: ""
end