# typed: strict
module Domain::UsersHelper
  extend T::Sig
  extend T::Helpers
  include HelpersInterface
  include Pundit::Authorization
  abstract!

  sig { params(user: Domain::User).returns(T.nilable(String)) }
  def user_avatar_path_for_view(user)
    if avatar = user.avatar
      domain_user_avatar_img_src_path(avatar, thumb: "32-avatar")
    end
  end

  sig { params(user: Domain::User).returns(T.nilable(String)) }
  def user_name_for_view(user)
    if avatar = user.avatar
      domain_user_avatar_img_src_path(avatar, thumb: "32-avatar")
    end
  end

  sig { params(user: Domain::User).returns(T.nilable(String)) }
  def domain_user_registered_at_string_for_view(user)
    ts = domain_user_registered_at_ts_for_view(user)
    ts ? time_ago_in_words(ts) : nil
  end

  sig do
    params(user: Domain::User).returns(T.nilable(ActiveSupport::TimeWithZone))
  end
  def domain_user_registered_at_ts_for_view(user)
    case user
    when Domain::User::FaUser, Domain::User::E621User
      user.registered_at
    else
      nil
    end
  end

  sig do
    params(
      avatar: T.nilable(Domain::UserAvatar),
      thumb: T.nilable(String),
    ).returns(String)
  end
  def domain_user_avatar_img_src_path(avatar, thumb: nil)
    cache_key = ["domain_user_avatar_img_src_path", avatar&.id, thumb]
    Rails
      .cache
      .fetch(cache_key, expires_in: 1.day) do
        if (sha256 = avatar&.log_entry&.response_sha256)
          Rails.application.routes.url_helpers.blob_path(
            HexUtil.bin2hex(sha256),
            format: "jpg",
            thumb: thumb,
          )
        elsif avatar && avatar.state_file_404? &&
              (sha256 = avatar.last_log_entry&.response_sha256)
          Rails.application.routes.url_helpers.blob_path(
            HexUtil.bin2hex(sha256),
            format: "jpg",
            thumb: thumb,
          )
        else
          # default / 'not found' avatar image
          # "/blobs/9080fd4e7e23920eb2dccfe2d86903fc3e748eebb2e5aa8c657bbf6f3d941cdc/contents.jpg"
          asset_path("user-circle.svg")
        end
      end
  end
  sig do
    params(
      avatar: T.nilable(Domain::UserAvatar),
      thumb: T.nilable(String),
    ).returns(String)
  end
  def domain_user_avatar_img_tag(avatar, thumb: nil)
    if (sha256 = avatar&.log_entry&.response_sha256)
      image_tag(
        blob_path(HexUtil.bin2hex(sha256), format: "jpg", thumb: thumb),
        class: "inline-block h-4 w-4 flex-shrink-0 rounded-sm object-cover",
        alt: avatar&.user&.name_for_view || "user avatar",
      )
    else
      raw <<~SVG
      <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
        <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z" />
      </svg>
      SVG
    end
  end

  sig { params(user: Domain::User).returns(String) }
  def site_icon_path_for_user(user)
    case user
    when Domain::User::E621User
      asset_path("domain-icons/e621.png")
    when Domain::User::FaUser
      asset_path("domain-icons/fa.png")
    when Domain::User::InkbunnyUser
      asset_path("domain-icons/inkbunny.png")
    when Domain::User::SofurryUser
      asset_path("domain-icons/sofurry.png")
    else
      Kernel.raise "Unknown user type: #{user.class}"
    end
  end

  sig { params(user: Domain::User).returns(String) }
  def domain_user_path(user)
    "#{domain_users_path}/#{user.to_param}"
  end

  sig { returns(String) }
  def domain_users_path
    "/users"
  end

  sig { params(user: Domain::User).returns(String) }
  def domain_user_posts_path(user)
    "#{domain_user_path(user)}/posts"
  end

  sig { params(user: Domain::User).returns(String) }
  def domain_user_favorites_path(user)
    "#{domain_user_path(user)}/favorites"
  end

  sig { params(user: Domain::User).returns(String) }
  def domain_user_followed_by_path(user)
    "#{domain_user_path(user)}/followed_by"
  end

  sig { params(user: Domain::User).returns(String) }
  def domain_user_following_path(user)
    "#{domain_user_path(user)}/following"
  end

  sig { params(user: Domain::User, kind: String).returns(String) }
  def tracked_objects_domain_user_job_events_path(user, kind:)
    unless Domain::UserJobEvent::AddTrackedObject.kinds.include?(kind)
      Kernel.raise "invalid kind: #{kind}"
    end
    "#{domain_user_path(user)}/job_events/tracked_objects/#{kind}"
  end

  sig { params(user: Domain::User).returns(String) }
  def tracked_objects_domain_user_job_events_kinds_path(user)
    "#{domain_user_path(user)}/job_events/tracked_objects"
  end

  sig { params(user: Domain::User, kind: String).returns(String) }
  def enqueue_scan_job_domain_user_job_events_path(user, kind:)
    unless Domain::UserJobEvent::AddTrackedObject.kinds.include?(kind)
      Kernel.raise "invalid kind: #{kind}"
    end
    "#{domain_user_path(user)}/job_events/enqueue_scan_job/#{kind}"
  end

  class StatRow < T::ImmutableStruct
    include T::Struct::ActsAsComparable

    const :name, String
    const :value,
          T.nilable(
            T.any(String, Integer, HasTimestampsWithDueAt::TimestampScanInfo),
          )
    const :link_to, T.nilable(String)
    const :fa_icon_class, T.nilable(String)
    const :hover_title, T.nilable(String)
  end

  sig { params(user: Domain::User).returns(T::Array[StatRow]) }
  def stat_rows_for_user(user)
    rows = T.let([], T::Array[StatRow])
    if user.has_faved_posts?
      rows << StatRow.new(name: "Favorites", value: user.user_post_favs.size)
    end
    if user.has_followed_by_users?
      can_view_link = policy(user).followed_by?
      rows << StatRow.new(
        name: "Followed by",
        value: user.user_user_follows_to.size,
        link_to: can_view_link ? domain_user_followed_by_path(user) : nil,
      )
    end
    if user.has_followed_users?
      can_view_link = policy(user).following?
      rows << StatRow.new(
        name: "Following",
        value: user.user_user_follows_from.size,
        link_to: can_view_link ? domain_user_following_path(user) : nil,
      )
    end

    can_view_timestamps = policy(user).view_page_scanned_at_timestamps?
    can_view_log_entries = policy(user).view_log_entries?
    icon_for =
      Kernel.proc do |due_for_scan|
        due_for_scan ? "fa-hourglass-half" : "fa-check"
      end

    if user.is_a?(Domain::User::FaUser) && can_view_timestamps
      if can_view_log_entries && hle = user.guess_last_user_page_log_entry
        rows << StatRow.new(
          name: "Page scanned",
          value: user.page_scan,
          link_to: log_entry_path(hle),
          fa_icon_class: icon_for.call(user.page_scan.due?),
          hover_title: user.page_scan.interval.inspect,
        )
      else
        rows << StatRow.new(
          name: "Page",
          value: user.page_scan,
          fa_icon_class: icon_for.call(user.page_scan.due?),
          hover_title: user.page_scan.interval.inspect,
        )
      end

      rows << StatRow.new(
        name: "Favs",
        value: user.favs_scan,
        link_to:
          tracked_objects_domain_user_job_events_path(user, kind: "favs"),
        fa_icon_class: icon_for.call(user.favs_scan.due?),
        hover_title: user.favs_scan.interval.inspect,
      )
      rows << StatRow.new(
        name: "Followed by",
        value: user.followed_by_scan,
        fa_icon_class: icon_for.call(user.followed_by_scan.due?),
        hover_title: user.followed_by_scan.interval.inspect,
      )
      rows << StatRow.new(
        name: "Following",
        value: user.follows_scan,
        fa_icon_class: icon_for.call(user.follows_scan.due?),
        hover_title: user.follows_scan.interval.inspect,
      )
      rows << StatRow.new(
        name: "Gallery",
        value: user.gallery_scan,
        fa_icon_class: icon_for.call(user.gallery_scan.due?),
        hover_title: user.gallery_scan.interval.inspect,
      )
    elsif user.is_a?(Domain::User::E621User) && can_view_timestamps
      if user.favs_are_hidden
        rows << StatRow.new(name: "Favorites hidden", value: "yes")
      else
        rows << StatRow.new(
          name: "Server favorites",
          value: user.num_other_favs_cached,
        )
      end
      rows << StatRow.new(
        name: "Favorites",
        value: user.favs_scan,
        fa_icon_class: icon_for.call(user.favs_scan.due?),
        hover_title: user.favs_scan.interval.inspect,
      )
    end

    rows
  end
end
