# frozen_string_literal: true

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

module Qti
  class CalculatedInteraction < AssessmentItemConverter
    def initialize(opts)
      super
      @question[:answers] = []
      @question[:variables] = []
      @question[:question_type] = "calculated_question"
    end

    def parse_question_data
      imported_formula = @doc.at_css("calculated formula")
      @question[:imported_formula] = CGI.unescape(imported_formula.text) if imported_formula
      get_calculated_property("answer_tolerance")
      if @question[:answer_tolerance] && !@question[:answer_tolerance].to_s.match(/[^\d.]/)
        @question[:answer_tolerance] = @question[:answer_tolerance].to_f
      end

      get_calculated_property("unit_points_percent")
      @question[:unit_points_percent] = @question[:unit_points_percent].to_f if @question[:unit_points_percent]
      get_calculated_property("unit_value")
      get_calculated_property("unit_required", true)
      get_calculated_property("unit_case_sensitive", true)
      get_calculated_property("partial_credit_points_percent")
      @question[:partial_credit_points_percent] = @question[:partial_credit_points_percent].to_f if @question[:partial_credit_points_percent]
      get_calculated_property("partial_credit_tolerance")
      @question[:partial_credit_tolerance] = @question[:partial_credit_tolerance].to_f if @question[:partial_credit_tolerance]

      get_variables
      get_answer_sets
      get_feedback
      get_formulas

      if !@question[:answer_tolerance] &&
         (tolerance = get_node_att(@doc, "instructureMetadata instructureField[name=formula_tolerance]", "value"))
        @question[:answer_tolerance] = tolerance
      end
      if !@question[:formula_decimal_places] &&
         (precision = get_node_att(@doc, "instructureMetadata instructureField[name=formula_precision]", "value"))
        @question[:formula_decimal_places] = precision.to_i
      end

      apply_d2l_fixes if @flavor == Qti::Flavors::D2L

      if @question[:formulas]&.empty? && @question[:imported_formula]
        @question[:formulas] << { formula: @question[:imported_formula] }
      end
      @question
    end

    def get_calculated_property(prop_name, is_true_false = false)
      @question[:"#{prop_name}"] = @doc.at_css("calculated #{prop_name}").text if @doc.at_css("calculated #{prop_name}")
      if is_true_false && @question[:"#{prop_name}"]
        @question[:"#{prop_name}"] = @question[:"#{prop_name}"] == "true"
      end
    end

    def get_variables
      @doc.css("calculated vars var").each do |v|
        var = {}
        @question[:variables] << var
        var[:name] = v["name"]
        var[:scale] = v["scale"].to_i
        var[:min] = v.at_css("min").text.to_f if v.at_css("min")
        var[:max] = v.at_css("max").text.to_f if v.at_css("max")
      end
    end

    def get_answer_sets
      @doc.css("calculated var_sets var_set").each do |vs|
        set = { variables: [], weight: 100 }
        set[:id] = vs["ident"].presence || unique_local_id
        @question[:answers] << set
        set[:answer] = vs.at_css("answer").text.to_f if vs.at_css("answer")

        vs.css("var").each do |v|
          var = {}
          set[:variables] << var
          var[:name] = v["name"]
          var[:value] = v.text.to_f
        end
      end
    end

    def get_formulas
      @question[:formulas] = []
      if (formulas_node = @doc.at_css("formulas"))
        @question[:formula_decimal_places] = formulas_node["decimal_places"].to_i
        formulas_node.css("formula").each do |f_node|
          formula = {}
          formula[:formula] = f_node.text
          @question[:formulas] << formula
        end
      end
      @question[:formulas]
    end

    def apply_d2l_fixes
      @question[:variables].each do |v|
        v_name = v[:name]
        # substitute {var} for [var]
        @question[:question_text]&.gsub!("{#{v_name}}", "[#{v_name}]")
        # substitute {var} for var
        @question[:imported_formula]&.gsub!("{#{v_name}}", v_name.to_s)
      end
      if @question[:imported_formula]
        method_substitutions = { "sqr" => "sqrt", "Factorial" => "fact", "exp" => "e" }
        method_substitutions.each do |orig_method, new_method|
          @question[:imported_formula].gsub!("#{orig_method}(", "#{new_method}(")
        end
      end
      if @question[:variables].count == 1
        # is this secretly a simple numeric question in disguise
        var = @question[:variables].first
        if (var[:min] == var[:max]) && (@question[:imported_formula] == var[:name]) # yup the formula for the answer is "x" and there's only one possible value
          %i[variables formulas imported_formula formula_decimal_places answer_tolerance].each { |k| @question.delete(k) }
          @question[:question_type] = "numerical_question"
          @question[:answers] = [
            { weight: 100,
              id: unique_local_id,
              text: "answer_text",
              numerical_answer_type: "exact_answer",
              exact: var[:min] }
          ]
        end
      end
    end
  end
end
