class EdFi::Indiana::Sandbox::ApplicationService
  def self.call(*args, &block)
    service = new(*args, &block)
    service.destroy_logs if defined?(service.endpoint)
    service.call
  end

  def initialize(school_year_id, params={})
    @school_year_id = school_year_id
    @params = params
    set_time_zone
  end

  def valid_token?(credentials=nil)
    access_token(credentials).present?
  end

  # ed_fi_id is a EdFi::Id Object
  def delete_from_dex(ed_fi_id)
    response = rest_client_request(:delete, "#{url}/#{ed_fi_id.number}", nil)
    ed_fi_id.delete if response
  end

  def find_dex_record(ed_fi_id)
    record = rest_client_request(
      :get,
      "#{url}/#{ed_fi_id.number}",
      nil,
      header_params: {}
    )
    record ? JSON.parse(record) : nil
  end

  def update_dex_record(ed_fi_id, data)
    response = rest_client_request(
      :put,
      "#{url}/#{ed_fi_id.number}",
      nil,
      body: data.to_json
    )
    response.code == 204 ? record_props(data) : nil
  end

  def retrieve_and_update_dex_record(ed_fi_id, updated_data)
    record = find_dex_record(ed_fi_id)
    return unless record

    # Modify the record with updated_data
    record.merge!(updated_data)

    update_dex_record(ed_fi_id, record)
  end

  def destroy_logs
    tmp_endpoint = endpoint.to_s.split('/').last
    school_year.ed_fi_logs.where(endpoint: tmp_endpoint).delete_all
  end

  def edfi_school?
    return if state_id.blank?

    RestClient.get("#{api_url}/ed-fi/schools?schoolId=#{state_id.number}", headers)
  rescue
    false
  end

  private
    def set_time_zone
      Time.zone = school.time_zone
    end

    def environment
      school.find_or_build_ed_fi_indiana_environment.environment.to_sym
    end

    def indiana_secrets
      Rails.application.secrets.edfi[:indiana][environment]
    end

    def base_url
      indiana_secrets[:base_url]
    end

    def api_url
      "#{base_url}/#{indiana_secrets[:url_version]}/#{school_year.academic_year}"
    end

    def url
      "#{api_url}/#{endpoint}"
    end

    def school_year
      @school_year ||= SchoolYear.find(@school_year_id)
    end

    def school
      @school ||= school_year.school
    end

    def state_id
      @state_id ||= school.state_id
    end

    def reporting_config
      @reporting_config ||= school_year.state_reporting_config
    end

    def school_config
      @school_config ||= school.school_config
    end

    def credential
      @credential ||= school.find_or_build_ed_fi_credential
    end

    def organization_id
      @organization_id ||= school.additional_ids.school.first
    end

    def access_token(credentials=nil)
      @access_token ||= request_token(credentials)['access_token'] if request_token(credentials)
    end

    def school_year_range
      school_year.q1_start..school_year.end
    end

    def school_year_students
      school_year.school_year_students
        .preload(:student)
        .joins(:student)
        .where.not(Students: { StateID: [nil, '', 0] })
        .where.not(grade_id: grades_mapped_ungraded)
    end

    def school_year_students_with_association
      school_year_students.where.not(edfi_id: [nil, '', 0])
    end

    def grades_mapped_ungraded
      school.grades.where(edfi_grade: -4).pluck(:grade)
    end

    def descriptor_service(descriptor)
      EdFi::Indiana::Sandbox::DescriptorService.call(school_year.id, descriptor: descriptor)
    end

    def create_or_update(model, data)
      ed_fi_id = model.ed_fi_id&.number
      if ed_fi_id
        method = :put
        path = "#{url}/#{ed_fi_id}"
      else
        method = :post
        path = url
      end

      response = rest_client_request(method, path, model, body: data.to_json)
      return if ed_fi_id || response.nil?

      edfi_id = response.headers[:location].split('/').last

      # Remove leading namespace from endpoint
      tmp_endpoint = endpoint.to_s.split('/').last

      model.find_or_build_ed_fi_id
        .update(number: edfi_id, endpoint: tmp_endpoint, school_year: school_year)
    end

    def request_token(credentials=nil)
      body = {
        client_id: credentials&.fetch('key', nil) || credential.key,
        client_secret: credentials&.fetch('secret', nil) || credential.secret,
        grant_type: :client_credentials
      }

      resp = begin RestClient.post("#{base_url}/oauth/token", body)
      rescue RestClient::Exception => e
        nil
      end
      JSON.parse(resp) if resp
    end

    def headers
      { Authorization: "Bearer #{access_token}", 'Content-Type' => 'application/json' }
    end

    def rest_client_request(method, path, model, body: {}, header_params: {})
      RestClient::Request.execute(
        method: method,
        url: path,
        headers: headers.merge(header_params),
        payload: body
      )
    rescue RestClient::Exception => e
      return if e.response&.body.blank? || !model

      # Remove leading namespace from endpoint
      tmp_endpoint = endpoint.to_s.split('/').last
      model.ed_fi_logs.create(
        school_year: school_year,
        endpoint: tmp_endpoint,
        json: body,
        response: e.response.body,
        status_code: e.response.code
      )
      nil
    end
end
