require 'open-uri' # allows to load URLs using open()
require 'json'
require 'deb_importer'

class Package < ApplicationRecord
  # PostgreSQL-based full-text search:
  # https://github.com/Casecommons/pg_search
  include PgSearch::Model
  # TODO: Make search weighted on users' rating
  pg_search_scope :general_search,
                  # :against => [:name, :description, :long_description],
                  against: [
                    [:name, 'A'],
                    [:description, 'B'],
                    [:long_description, 'C']
                  ],
                  using: {
                    tsearch: { dictionary: 'english' }
                  }

  # I am using "destroy_all" here so that when a package gets destroys the
  # callbacks for all dependent screenshots are executed - thus removing the
  # screenshot files from disk.
  has_many :screenshots,
           -> { order(id: :desc) },
           inverse_of: :package,
           dependent: :destroy

  # default_scope {
  # order('name ASC')
  # }

  # Define the parameter(s) used for a /package/:name URL to an instance of this model
  def to_param
    name
  end

  # Return the first paragraph of the long description.
  def long_description_first_paragraph
    long_description.split(/\n\.\n/).first if long_description
  end

  # Return a query of all packages that have screenshots
  def self.with_screenshots
    # Query for all packages who's ID appears in a screenshot's "package_id" field
    subselect = Screenshot.select(:package_id)
    where(id: subselect)
  end

  # Return a query of all packages that have approved screenshots
  def self.with_public_screenshots
    # Query for all packages who's ID appears in a screenshot's "package_id" field
    subselect = Screenshot.visible.approved.select(:package_id)
    where(id: subselect)
  end

  # Return a list of packages that have screenshots to be moderated
  def self.need_moderation
    Package.joins(:screenshots).where('screenshots.approved=false').distinct(:name)
  end

  # Return a query of all packages that do not have screenshots
  def self.without_screenshots
    # Query for all packages who's ID does not appear in a screenshot's "package_id" field
    subselect = Screenshot.select(:package_id)
    where.not(id: subselect)
  end

  # Get all screenshots and have them sorted descendingly by their version number (Debian style).
  def screenshots_sorted_by_version
    screenshots.approved.to_a.sort { |x, y| version_compare(x.version, y.version) }
  end

  # Return all approved screenshots for this package
  def approved_screenshots
    screenshots.approved
  end

  # Return the newest screenshot that is not newer than the given version.
  # This algorithm collects all image
  # versions of a package and determines the (second) newest version.
  # E.g. if there are version 1.0 and 2.0 and the user is looking for
  # a screenshot of version 1.5 then the 1.0 version is returned.
  # This way the user does not see a screenshot of version 2.0 because
  # 2.0 might contain features that were not there in version 1.5.
  def best_screenshot_for_version(version)
    sorted_screenshots = screenshots_sorted_by_version
    sorted_screenshots.each do |ss|
      logger.debug { "Comparing version #{version} against #{ss.version}" }
      return ss if version_compare(version, ss.version) <= 0
    end
    sorted_screenshots.last
  end

  # Return the part of the version up to the first - or +
  def upstream_version
    version.split(/[-+]/).first
  end

  private

  def version_compare(x, y)
    x = '0' unless x.present?
    y = '0' unless y.present?
    version_x = DebImporter::Version.new(x)
    version_y = DebImporter::Version.new(y)
    if version_x < version_y
      1
    elsif version_x > version_y
      -1
    else
      0
    end
  end
end
