# frozen_string_literal: true

class IncomingLink < ActiveRecord::Base
  belongs_to :post
  belongs_to :user
  belongs_to :incoming_referer

  validate :referer_valid
  validates :post_id, presence: true

  after_create :update_link_counts

  attr_accessor :url

  def self.add(opts)
    user_id, host, referer = nil
    current_user = opts[:current_user]

    username = opts[:username]
    username = nil if !(String === username)
    username = nil if username&.include?("\0")
    if username
      u = User.select(:id).find_by(username_lower: username.downcase)
      user_id = u.id if u
    end
    ip_address = opts[:ip_address]

    if opts[:referer].present?
      begin
        host = URI.parse(opts[:referer]).host
        referer = opts[:referer][0..999]
      rescue URI::Error
        # bad uri, skip
      end
    end

    if host != opts[:host] && (user_id || referer)
      post_id = opts[:post_id]
      post_id ||=
        Post.where(topic_id: opts[:topic_id], post_number: opts[:post_number] || 1).pick(:id)

      cid = current_user ? (current_user.id) : (nil)
      ip_address = nil if cid

      unless cid && cid == user_id
        if post_id
          create(
            referer: referer,
            user_id: user_id,
            post_id: post_id,
            current_user_id: cid,
            ip_address: ip_address,
          )
        end
      end
    end
  end

  def referer=(referer)
    self.incoming_referer_id = nil

    # will set incoming_referer_id
    return if referer.blank?

    parsed = URI.parse(referer)

    if parsed.scheme == "http" || parsed.scheme == "https"
      domain = IncomingDomain.add!(parsed)

      referer_record = IncomingReferer.add!(path: parsed.path, incoming_domain: domain) if domain
      self.incoming_referer_id = referer_record.id if referer_record
    end
  rescue URI::Error
    # ignore
  end

  def referer
    if self.incoming_referer
      self.incoming_referer.incoming_domain.to_url + self.incoming_referer.path
    end
  end

  def domain
    incoming_referer.incoming_domain.name if incoming_referer
  end

  # Internal: Update appropriate link counts.
  def update_link_counts
    DB.exec(
      "UPDATE topics
              SET incoming_link_count = incoming_link_count + 1
              WHERE id = (SELECT topic_id FROM posts where id = ?)",
      post_id,
    )
    DB.exec(
      "UPDATE posts
              SET incoming_link_count = incoming_link_count + 1
              WHERE id = ?",
      post_id,
    )
  end

  protected

  def referer_valid
    return true unless referer
    if (referer.length < 3 || referer.length > 100) || (domain.length < 1 || domain.length > 100)
      # internal, no need to localize
      errors.add(:referer, "referer is invalid")
      false
    else
      true
    end
  end
end

# == Schema Information
#
# Table name: incoming_links
#
#  id                  :integer          not null, primary key
#  created_at          :datetime         not null
#  user_id             :integer
#  ip_address          :inet
#  current_user_id     :integer
#  post_id             :integer          not null
#  incoming_referer_id :integer
#
# Indexes
#
#  index_incoming_links_on_created_at_and_user_id  (created_at,user_id)
#  index_incoming_links_on_post_id                 (post_id)
#