# frozen_string_literal: true

#
# Copyright (C) 2015 - 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/>.
#

# Filters added to this controller apply to all controllers in the application.
# Likewise, all the methods added will be available for all controllers.

module Lti
  class ContentItemResponse
    MEDIA_TYPES = %i[assignments discussion_topics modules module_items pages quizzes files].freeze
    SUPPORTED_EXPORT_TYPES = %w[common_cartridge].freeze

    def initialize(context, controller, current_user, media_types, export_type)
      @context = context
      @controller = controller # for url generation
      @current_user = current_user
      @media_types = media_types.with_indifferent_access
      @export_type = export_type || "common_cartridge" # legacy API behavior defaults to common cartridge

      raise Lti::Errors::InvalidMediaTypeError unless media_types_valid?
      raise Lti::Errors::UnsupportedExportTypeError unless SUPPORTED_EXPORT_TYPES.include? @export_type
    end

    def query_params
      unless @query_params
        select = {}
        @media_types.each { |k, v| select[k] = v }
        @query_params = { "export_type" => @export_type }
        @query_params["select"] = select if select.present?
      end
      @query_params
    end

    def media_type
      unless @media_type
        if module_item?
          case tag.content
          when Assignment
            @media_type = "assignment"
          when DiscussionTopic
            @media_type = "discussion_topic"
          when Quizzes::Quiz
            @media_type = "quiz"
          when WikiPage
            @media_type = "page"
          end
        else
          @media_type = canvas_media_type
        end
      end
      @media_type
    end

    def tag
      if !@tag && @media_types.include?(:module_items)
        @tag = @context.context_module_tags.where(id: @media_types[:module_items].first).first
      end
      @tag
    end

    def file
      return unless @media_types.include? :files

      unless @file
        @file = Attachment.where(id: @media_types[:files].first).first
        if @context.is_a?(Account)
          raise ActiveRecord::RecordNotFound unless @file.context == @current_user
        elsif @file.context.is_a?(Course)
          raise ActiveRecord::RecordNotFound unless @file.context == @context
        elsif @file.context.is_a?(Group)
          raise ActiveRecord::RecordNotFound unless @file.context.context == @context
        end
        raise Lti::Errors::UnauthorizedError if @file.locked_for?(@current_user, check_policies: true)
      end
      @file
    end

    def title
      @title ||= case canvas_media_type
                 when "file"
                   file.display_name
                 when "assignment"
                   assignment_from_context.title
                 when "discussion_topic"
                   @context.discussion_topics.where(id: @media_types[:discussion_topics].first).first.title
                 when "module"
                   @context.context_modules.where(id: @media_types[:modules].first).first.name
                 when "page"
                   @context.wiki_pages.where(id: @media_types[:pages].first).first.title
                 when "module_item"
                   tag.title
                 when "quiz"
                   @context.quizzes.where(id: @media_types[:quizzes].first).first.title
                 when "course"
                   @context.name
                 end
    end

    def media_type_for_content_type
      return media_type unless Account.site_admin.feature_enabled?(:new_quizzes_media_type)
      return media_type unless assignment?

      assignment = module_item? ? tag.content : assignment_from_context
      assignment.quiz_lti? ? "quizzesnext" : "assignment"
    end

    def content_type
      @content_type ||= @media_types.include?(:files) ? file.content_type : "application/vnd.instructure.api.content-exports.#{media_type_for_content_type}"
    end

    def url
      @url ||= @media_types.include?(:files) ? @controller.file_download_url(file, { verifier: file.uuid, download: "1", download_frd: "1" }) : @controller.api_v1_course_content_exports_url(@context) + "?" + query_params.to_query
    end

    def as_json(opts = {})
      case opts[:lti_message_type]
      when "ContentItemSelectionResponse"
        content_item_selection_response_json
      when "ContentItemSelection"
        content_item_selection_json
      else
        raise Lti::Errors::UnsupportedMessageTypeError
      end
    end

    private

    def assignment?
      media_type == "assignment"
    end

    def module_item?
      canvas_media_type == "module_item"
    end

    def assignment_from_context
      @context.assignments.where(id: @media_types[:assignments].first).first
    end

    def canvas_media_type
      @canvas_media_type ||= if @media_types.keys.size == 1
                               @media_types.keys.first.to_s.singularize
                             else
                               "course"
                             end
    end

    def media_types_valid?
      @media_types.each do |type, ids|
        scope = case type
                when "files"
                  Attachment
                when "assignments"
                  @context.assignments
                when "discussion_topics"
                  @context.discussion_topics
                when "modules"
                  @context.context_modules
                when "pages"
                  @context.wiki_pages
                when "module_items"
                  @context.context_module_tags
                when "quizzes"
                  @context.quizzes
                end

        return false if scope.where(id: ids).count != ids.count
      end
      true
    end

    ##
    # This message type is deprecated, please use content_item_selection_json
    ##
    def content_item_selection_response_json
      {
        "@context" => "http://purl.imsglobal.org/ctx/lti/v1/ContentItemPlacement",
        "@graph" => [
          {
            "@type" => "ContentItemPlacement",
            "placementOf" => {
              "@type" => "FileItem",
              "@id" => url,
              "mediaType" => content_type,
              "title" => title
            }
          }
        ]
      }
    end

    def content_item_selection_json
      {
        "@context" => "http://purl.imsglobal.org/ctx/lti/v1/ContentItem",
        "@graph" => [
          {
            "@type" => "FileItem",
            "url" => url,
            "mediaType" => content_type,
            "title" => title,
            "copyAdvice" => true
          }
        ]
      }
    end
  end
end
