# frozen_string_literal: true

#
# Copyright (C) 2016 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.

class OriginalityReport < ActiveRecord::Base
  include Rails.application.routes.url_helpers

  belongs_to :submission
  belongs_to :attachment
  belongs_to :originality_report_attachment, class_name: "Attachment"
  belongs_to :root_account, class_name: "Account"

  has_one :lti_link, class_name: 'Lti::Link', as: :linkable, inverse_of: :linkable, dependent: :destroy
  accepts_nested_attributes_for :lti_link, allow_destroy: true

  validates :submission, presence: true
  validates :workflow_state, inclusion: { in: ['scored', 'error', 'pending'] }
  validates :originality_score, inclusion: { in: 0..100, message: 'score must be between 0 and 100' }, allow_nil: true

  alias_attribute :file_id, :attachment_id
  alias_attribute :originality_report_file_id, :originality_report_attachment_id
  before_validation :infer_workflow_state
  after_validation :set_submission_time
  before_save :set_root_account

  def self.submission_asset_key(submission)
    "#{submission.asset_string}_#{submission.submitted_at.utc.iso8601}"
  end

  def state
    if workflow_state != 'scored'
      return workflow_state
    end

    Turnitin.state_from_similarity_score(originality_score)
  end

  def as_json(options = nil)
    super(options).tap do |h|
      h[:file_id] = h.delete :attachment_id
      h[:originality_report_file_id] = h.delete :originality_report_attachment_id
      if lti_link.present?
        h[:tool_setting] = { resource_url: lti_link.resource_url,
                             resource_type_code:lti_link.resource_type_code }
      end
    end
  end

  def report_launch_path
    if lti_link.present?
      course_assignment_resource_link_id_path(course_id: assignment.context_id,
                                             assignment_id: assignment.id,
                                             resource_link_id: lti_link.resource_link_id,
                                             host: HostUrl.context_host(assignment.context),
                                             display: 'borderless')
    else
      originality_report_url
    end
  end

  def asset_key
    return Attachment.asset_string(attachment_id) if attachment_id.present?
    if submission_time.present?
      "#{Submission.asset_string(submission_id)}_#{submission_time&.utc&.iso8601}"
    else
      Submission.asset_string(submission_id)
    end
  end

  def self.copy_to_group_submissions!(report_id: , user_id: )
    report = self.find(report_id)
    report.copy_to_group_submissions!
  rescue ActiveRecord::RecordNotFound => e
    user = User.where(id: user_id).first
    if user.nil? || user.fake_student?
      # Test students get reset frequently, which wipes out their originality
      # reports, so if we can't find a report but the user is also
      # gone or is known to be a fake_student, we can ignore this and not
      # continue with the job
      Canvas::Errors.capture(e, { report_id: report_id, user_id: user_id }, :info)
    else
      raise e
    end
  end

  def copy_to_group_submissions!
    return if submission.group_id.blank?
    group_submissions = assignment.submissions.where.not(id: submission.id).where(group: submission.group)
    group_submissions.find_each do |s|
      copy_of_report = self.dup
      copy_of_report.submission_time = nil

      # We don't want a single submission to have
      # multiple originality reports with the same
      # attachment/submission combo hanging around.
      s.originality_reports.where(
        attachment_id: attachment_id
      ).destroy_all

      copy_of_report.update!(submission: s)
      lti_link&.dup&.update!(
        linkable: copy_of_report,
        resource_link_id: nil
      )
    end
  end

  private

  def assignment
    submission.assignment
  end

  def set_submission_time
    self.submission_time ||= submission.reload.submitted_at
  end

  def infer_workflow_state
    self.workflow_state = 'error' if error_message.present?
    return if self.workflow_state == 'error'
    self.workflow_state = self.originality_score.present? ? 'scored' : 'pending'
  end

  def set_root_account
    self.root_account_id ||= submission&.root_account_id
  end
end
