# frozen_string_literal: true

class ProcessStatusReferenceService
  def call(status, **options)
    @status = status

    urls = parse_urls(status, options).union(options[:urls]) - [ActivityPub::TagManager.instance.uri_for(status), ActivityPub::TagManager.instance.url_for(status)]
    process_reference(urls, (options[:status_reference_ids] || []).compact.uniq, status.id)
  end

  private

  def parse_urls(status, **options)
    if status.local?
      parse_local_urls(status.text)
    else
      mentions = options[:mentions] || status.mentions
      parse_remote_urls(status.text, mentions)
    end
  end

  def process_reference(urls, ids, status_id)
    target_statuses = urls_to_target_statuses(urls, status_id).uniq
    target_statuses = target_statuses + Status.where(id: ids - target_statuses.map(&:id)).where(visibility: [:public, :unlisted, :private])

    references = target_statuses.filter_map do |target_status|
      StatusReference.create(status_id: status_id, target_status_id: target_status.id)
    end

    references.group_by{|reference| reference.target_status.account}.each do |account, grouped_references|
      create_notification(grouped_references.sort_by(&:id).first) if account.local?
    end
  end

  def create_notification(reference)
    NotifyService.new.call(reference.target_status.account, :status_reference, reference)
  end

  def parse_local_urls(text)
    text.scan(FetchLinkCardService::URL_PATTERN).map(&:second).uniq.filter_map do |url|
      Addressable::URI.parse(url).normalize.to_s
    rescue Addressable::URI::InvalidURIError
      nil
    end
  end

  def parse_remote_urls(text, mentions = [])
    html  = Nokogiri::HTML(text)
    links = html.css(':not(.reference-link-inline) > a')

    links.filter_map do |anchor|
      Addressable::URI.parse(anchor['href']).normalize.to_s unless skip_link?(anchor, mentions)
    rescue Addressable::URI::InvalidURIError
      nil
    end
  end

  def urls_to_target_statuses(urls, status_id)
    urls.uniq.filter_map do |url|
      url_to_target_status(url).tap do |target_status|
        if target_status.nil? && !TagManager.instance.local_url?(url)
          StatusReferenceResolveWorker.perform_async(status_id, url)
        end
      end
    end
  end

  def url_to_target_status(url)
    if TagManager.instance.local_url?(url)
      ActivityPub::TagManager.instance.uri_to_resource(url, Status)
    else
      EntityCache.instance.holding_status(url)
    end
  end

  def mention_link?(anchor, mentions)
    mentions.any? do |mention|
      anchor['href'] == ActivityPub::TagManager.instance.url_for(mention.account)
    end
  end

  def skip_link?(anchor, mentions)
    # Avoid links for hashtags and mentions (microformats)
    anchor['rel']&.include?('tag') || anchor['class']&.match?(/u-url|h-card/) || mention_link?(anchor, mentions)
  end
end
  