class EdFi::Indiana::V2026::CourseTranscriptService < EdFi::Indiana::V2026::ApplicationService
  def call
    period_long_transcripts.each { |c| transcript_props(c, c.classroom.course_number) }
    day_long_transcripts.each { |c| transcript_props(c, c.class_subject.code) }
    preschool_transcripts.each { |c| transcript_props(c, c.class_subject.code) }
  end

  def endpoint
    'ed-fi/coursetranscripts'
  end

  def find(id)
    records = rest_client_request(
      :get,
      url,
      nil,
      header_params: { params: { studentUniqueId: id } }
    )
    records ? JSON.parse(records).map { |r| record_props(r) } : []
  end

  def find_record(id)
    response = rest_client_request(:get, "#{url}/#{id}", nil)
    response ? JSON.parse(response) : {}
  end

  def delete(id)
    rest_client_request(:delete, "#{url}/#{id}", nil)
  end

  def destroy_logs
    nil
  end

  private
    def gpa_scales
      @gpa_scales ||= school.school_gpa_scales
    end

    def transcript_config
      @transcript_config ||= school.transcript_config
    end

    def grade_range
      {
        'preschool' => -10..-1,
        'primary' => 0..5,
        'middle' => 6..8,
        'secondary' => 9..12
      }[@params[:grade]]
    end

    def class_ids
      school.classrooms
        .preload(:class_periods)
        .joins(:school_state_classroom)
        .period_long
        .where.not(course_number: ['', ' '])
        .pluck(:id)
    end

    def subject_ids(type)
      school.class_subjects
        .joins(classroom: :school_state_classroom)
        .where.not(code: ['', ' '])
        .merge(Classroom.by_type(type))
        .pluck(:id)
    end

    def transcripts
      school.student_transcripts
        .where(grade: grade_range)
        .includes(:student, :college, :classroom)
        .by_school_year(school_year.id)
        .where.not(grade: grades_mapped_ungraded)
    end

    def period_long_transcripts
      transcripts
        .where(grade: 0..8)
        .or(transcripts.where(grade: 9..Float::INFINITY))
        .by_classes(class_ids)
    end

    def day_long_transcripts
      transcripts
        .most_recent(:quarter, school.id, school_year.id)
        .by_subjects(subject_ids(:day_long))
        .where(grade: 0..8, high_school_credit: false)
    end

    def preschool_transcripts
      transcripts
        .most_recent(:quarter, school.id, school_year.id)
        .by_subjects(subject_ids(:preschool))
        .where(grade: -10..0)
    end

    def class_students
      @class_students ||= school.class_students.group_by(&:student_id)
    end

    def terms
      @terms ||= if school.school_config.semesters?
        { 1 => 'Fall Semester', 2 => 'Spring Semester' }
      else
        { 1 => 'First Trimester', 2 => 'Second Trimester', 3 => 'Third Trimester' }
      end
    end

    def method_credit_earned_descriptors
      @method_credit_earned_descriptors ||= descriptor_service(:methodcreditearneddescriptors)
        .index_by { |d| d['id'] }
    end

    def method_credit_descriptor(transcript)
      descriptor = method_credit_earned_descriptors[transcript&.credit_descriptor_id]
      return 'uri://ed-fi.org/MethodCreditEarnedDescriptor#Classroom credit' if descriptor.nil?

      "#{descriptor['namespace']}##{descriptor['codeValue']}"
    end

    def transcript_props(transcript, code)
      classroom = transcript.classroom
      student = transcript.student
      semesters = if classroom.period_long? && transcript.semester.zero?
        credits = transcript.credits_earned / school_config.semesters.keys.count
        school_config.semesters.keys
      elsif classroom.preschool? || classroom.day_long?
        if school_config.trimesters?
          [transcript.quarter]
        elsif [1, 2].include?(transcript.quarter)
          [1]
        else
          [2]
        end
      else
        credits = transcript.credits_earned
        [transcript.semester]
      end

      grade_descriptor = if transcript.letter_grade == 'I'
        'uri://ed-fi.org/CourseAttemptResultDescriptor#Incomplete'
      elsif transcript.letter_grade == 'F'
        'uri://ed-fi.org/CourseAttemptResultDescriptor#Fail'
      elsif transcript.letter_grade.blank?
        'uri://doe.in.gov/CourseAttemptResultDescriptor#No grade awarded'
      else
        'uri://ed-fi.org/CourseAttemptResultDescriptor#Pass'
      end

      semesters.each do |semester|
        term_descriptor = if classroom.period_long?
          "uri://ed-fi.org/TermDescriptor##{terms[semester]}"
        else
          'uri://ed-fi.org/TermDescriptor#Year Round'
        end

        data = {}.tap do |props|
          props[:courseReference] = {
            educationOrganizationId: 1088000000,
            courseCode: code
          }
          props[:schoolReference] = { schoolId: state_id.number }
          props[:studentAcademicRecordReference] = {
            studentUniqueId: student&.state_number&.number,
            educationOrganizationId: organization_id.number,
            schoolYear: school_year.academic_year,
            termDescriptor: term_descriptor
          }
          if classroom.period_long? && !((0..8).cover?(transcript.grade) && !transcript.high_school_credit?)
            type = transcript.college ? 'Dual Credit' : 'Regular Credit'
            props[:earnedCreditTypeDescriptor] = "uri://doe.in.gov/CreditTypeDescriptor##{type}"
            props[:earnedCredits] = credits

            points = transcript.calculate_points(gpa_scales, transcript_config)
            props[:finalNumericGradeEarned] = points > 4 ? 4.0 : points.round(2)
          end

          props[:courseAttemptResultDescriptor] = grade_descriptor

          if transcript.college
            props[:externalEducationOrganizationReference] = {
              educationOrganizationId: "2000000#{transcript.college.code}"
            }
          end

          props[:methodCreditEarnedDescriptor] = method_credit_descriptor(transcript)
        end

        rest_client_request(:post, url, transcript, body: data.to_json)
      end
    end

    def record_props(response)
      {
        id: response['id'],
        code: response['courseReference']['courseCode'],
        courseAttemptResultDescriptor: response['courseAttemptResultDescriptor'].split('#')[1],
        termDescriptor: response['studentAcademicRecordReference']['termDescriptor'],
        courseCode: response['courseReference']['courseCode']
      }
    end
end
