require 'doorkeeper/grape/helpers'

module GestaoDeEstoque
  module V1
    class GestaoDeEstoques < Grape::API
      version 'v1', using: :path
      format :json
      prefix :api

      helpers Doorkeeper::Grape::Helpers

      before do
        unless Rails.env.development?
          doorkeeper_authorize! :gestao_de_estoques
        end
      end

      resource :listar_itens_de_genero_alimentar do
        desc 'Retorna a lista dos itens que são do subelemento de gênero de alimentação'
        get do
          itens = Base::Item.find_by_sql("
            SELECT DISTINCT item.id, item.descricao, COALESCE(item.descricao_tecnica, '') AS descricao_tecnica
            FROM base_itens item
            INNER JOIN base_categorias cat ON cat.id = item.categoria_id
            INNER JOIN base_elementos_por_categoria ele_cat ON ele_cat.categoria_id = cat.id
            INNER JOIN natureza_da_despesa_sub_elementos_de_despesa sub ON sub.id = ele_cat.elemento_de_gasto_id
            INNER JOIN natureza_da_despesa_elementos_de_despesa ele ON sub.elemento_de_despesa_id = ele.id
            WHERE ele.codigo = '30'
            AND sub.codigo = '07'
            ORDER BY item.descricao;
          ")

          JSON.parse(JSON.generate(itens.map{|item| item.attributes}))
        end
      end

      resource :listar_almoxarifados_da_educacao do
        desc 'Retorna a lista de almoxarifados da educação'
        params do
          requires :ano, type: Integer, desc: 'Ano do exercício fiscal'
        end

        get do
          ano = params[:ano]

          unless ano && ano > 0
            error!('Ano inválido', 400)
          end

          orcamento = Orcamento.find_by(exercicio: ano)

          unless orcamento
            error!('Orçamento não encontrado para o ano fornecido', 404)
          end

          almoxarifados = GestaoDeEstoque::Almoxarifado.find_by_sql("
            SELECT DISTINCT alm.id,alm.codigo, alm.nome, uo.id AS unidade_orcamentaria_id, uo.nome AS unidade_orcamentaria_descricao, alm.escola_id
            FROM gestao_de_estoque_almoxarifados alm
            INNER JOIN gestao_de_estoque_unidades_orcamentarias_do_almoxarifado uo_por_alm ON uo_por_alm.almoxarifado_id = alm.id
            INNER JOIN loa_unidades_orcamentarias uo ON uo.id = uo_por_alm.unidade_orcamentaria_id
            INNER JOIN loa_orgaos org ON org.id = uo.orgao_id
            WHERE alm.orcamento_id = #{orcamento.id}
            AND org.codigo = '08'
            AND uo.codigo = '10'
            ORDER BY alm.codigo
          ")

          JSON.parse(JSON.generate(almoxarifados.map{|almoxarifado| almoxarifado.attributes}))
        end
      end


      resource :listar_programas do
        desc 'Retorna a lista de programas disponíveis no sistema'
        params do
          optional :nome, type: String, desc: 'Nome do programa'
          optional :page, type: Integer, default: 1, desc: 'Número da página'
          optional :per_page, type: Integer, default: 10, desc: 'Itens por página'
        end

        get do
          page = params[:page]
          per_page = params[:per_page]

          query = GestaoDeEstoque::ProgramaEscolar.all
          query = query.where('nome ILIKE ?', "%#{params[:nome]}%") if params[:nome].present?
          query = query.joins(:programas_por_escolas).where("gestao_de_estoque_programa_por_escolas.escola_id = ?", params[:escola_id]) if params[:escola_id].present?
          result = query.map { |programa| { id: programa.id, nome: programa.nome , data_de_inicio: programa.data_inicial, data_de_fim: programa.data_final} }.paginate(page: page, per_page: per_page)

          {
            total: result.total_entries,
            total_pages: result.total_pages,
            current_page: result.current_page,
            per_page: per_page,
            programas: result
          }
        end
      end

      resource :listar_escolas do
        desc 'Retorna a lista de escolas no sistema'
        params do
          optional :nome, type: String, desc: 'Nome da escola'
          optional :id, type: Integer, desc: 'ID da escola'
          optional :page, type: Integer, default: 1, desc: 'Número da página'
          optional :per_page, type: Integer, default: 10, desc: 'Itens por página'
        end

        get do
          page = params[:page]
          per_page = params[:per_page]

          query = GestaoDeEstoque::Escola.all
          query = query.where('nome ILIKE ?', "%#{params[:nome]}%") if params[:nome].present?
          query = query.where('id = ?', params[:id]) if params[:id].present?
          result = query.map { |escola| { id: escola.id, nome: escola.nome} }.paginate(page: page, per_page: per_page)

          {
            total: result.total_entries,
            total_pages: result.total_pages,
            current_page: result.current_page,
            per_page: per_page,
            escolas: result
          }
        end
      end

      resource :listar_solicitacao_de_recebimentos_de_merenda_escolar do
        desc 'Retorna a lista de transferências de gênero alimentar que utilizam programas escolares'
        params do
          optional :id, type: Integer
          optional :escola_id, type: Integer
          optional :unidade_orcamentaria_origem_id, type: Integer
          optional :unidade_orcamentaria_destino_id, type: Integer
          optional :almoxarifado_origem_id, type: Integer
          optional :almoxarifado_destino_id, type: Integer
          optional :data_transferencia_inicio, type: Date
          optional :data_transferencia_fim, type: Date
          optional :page, type: Integer, default: 1
          optional :per_page, type: Integer, default: 10
        end

        get do
          page = params[:page]
          per_page = params[:per_page]

          sql = "
            SELECT DISTINCT transf.*
            FROM gestao_de_estoque_transferencias transf
            INNER JOIN gestao_de_estoque_programas_por_almoxarifado_e_transferencia transf_por_almoxarifado
              ON transf_por_almoxarifado.transferencia_id = transf.id
            INNER JOIN gestao_de_estoque_almoxarifados alm
              ON alm.id = transf_por_almoxarifado.almoxarifado_id
            INNER JOIN gestao_de_estoque_itens_do_programa_por_almoxarifado_e_transfer itens_por_prog_da_transf
              ON itens_por_prog_da_transf.programa_por_almoxarifado_e_transferencia_id = transf_por_almoxarifado.id
            INNER JOIN gestao_de_estoque_itens_da_transferencia itens_trans
              ON itens_trans.id = itens_por_prog_da_transf.item_da_transferencia_id
            INNER JOIN gestao_de_estoque_estoques estoque
              ON estoque.id = itens_trans.estoque_id
            INNER JOIN contabilidade_sub_elementos_de_despesa sub
              ON sub.id = estoque.sub_elemento_de_despesa_id
            INNER JOIN base_elementos_de_despesa ele
              ON ele.id = sub.elemento_de_despesa_id
            WHERE ele.codigo LIKE '____3000'
            AND sub.codigo = '07'
            AND transf.status = 1
          "
          sql << " AND transf.id = #{params[:id]}" if params[:id].present?
          sql << " AND alm.escola_id = #{params[:escola_id]}" if params[:escola_id].present?
          sql << " AND transf.unidade_orcamentaria_id = #{params[:unidade_orcamentaria_origem_id]}" if params[:unidade_orcamentaria_origem_id].present?
          sql << " AND transf.unidade_orcamentaria_de_destino_id = #{params[:unidade_orcamentaria_destino_id]}" if params[:unidade_orcamentaria_destino_id].present?
          sql << " AND transf.almoxarifado_id = #{params[:almoxarifado_origem_id]}" if params[:almoxarifado_origem_id].present?
          sql << " AND transf.almoxarifado_de_destino_id = #{params[:almoxarifado_destino_id]}" if params[:almoxarifado_destino_id].present?
          sql << " AND transf.data_de_transferencia >= '#{params[:data_transferencia_inicio]}'" if params[:data_transferencia_inicio].present?
          sql << " AND transf.data_de_transferencia <= '#{params[:data_transferencia_fim]}'" if params[:data_transferencia_fim].present?

          transferencias = GestaoDeEstoque::Transferencia.find_by_sql(sql).paginate(page: page, per_page: per_page)

          transferencias_escolares = transferencias.map do |transferencia|
            {
              id: transferencia.id,
              data: transferencia.data_de_transferencia,
              unidade_orcamentaria_origem_id: transferencia.try(:unidade_orcamentaria).try(:id),
              unidade_orcamentaria_origem_descricao: transferencia.try(:unidade_orcamentaria).try(:codigo_e_nome),
              almoxarifado_origem_id: transferencia.almoxarifado.id,
              almoxarifado_origem_descricao: transferencia.almoxarifado.codigo_e_nome,
              unidade_orcamentaria_destino_id: transferencia.try(:unidade_orcamentaria_de_destino).try(:id),
              unidade_orcamentaria_destino_descricao: transferencia.try(:unidade_orcamentaria_de_destino).try(:codigo_e_nome),
              almoxarifado_destino_id: transferencia.almoxarifado_de_destino.id,
              almoxarifado_destino_descricao: transferencia.almoxarifado_de_destino.codigo_e_nome,
              escolas: transferencia.programas_por_almoxarifado_e_transferencia.group_by { |t| t.programa_por_escola.escola }.map do |escola, transf_por_escola|
                {
                  id: escola.id,
                  nome: escola.nome,
                  programas: transf_por_escola.group_by { |t| t.programa_por_escola.programa }.map do |programa, transf_do_programa|
                    {
                      id: programa.id,
                      nome: programa.nome,
                      itens_por_programa_da_transferencia: transf_do_programa.flat_map { |t| t.itens_do_programa_por_almoxarifado_e_transferencia }.map do |item|
                        {
                          id: item.id,
                          item_id: item.item_id,
                          descricao: item.item.descricao,
                          quantidade: item.quantidade,
                          estoque_id: item.estoque_id
                        }
                      end
                    }
                  end
                }
              end
            }
          end

          {
            total: transferencias.total_entries,
            total_pages: transferencias.total_pages,
            current_page: transferencias.current_page,
            per_page: per_page,
            transferencias: transferencias_escolares
          }

        end
      end

      resource :confirmar_recebimentos_de_merenda_escolar do
        desc 'Confirma os recebimentos de gênero alimentar das transferências de merenda escolar'
        params do
          requires :transferencia, type: Hash do
            requires :id, type: Integer
            requires :escolas, type: Array do
              requires :id, type: Integer
              requires :programas, type: Array do
                requires :id, type: Integer
                requires :itens_por_programa_da_transferencia, type: Array do
                  requires :id, type: Integer
                  requires :quantidade, type: String
                  requires :estoque_id, type: Integer
                end
              end
            end
          end
        end

        post do
          transferencia_param = params[:transferencia]

          transferencia_solicitada = GestaoDeEstoque::Transferencia.find_by_sql(["
            SELECT DISTINCT transf.*
            FROM gestao_de_estoque_transferencias transf
            INNER JOIN gestao_de_estoque_programas_por_almoxarifado_e_transferencia transf_por_almoxarifado
              ON transf_por_almoxarifado.transferencia_id = transf.id
            INNER JOIN gestao_de_estoque_itens_do_programa_por_almoxarifado_e_transfer itens_por_prog_da_transf
              ON itens_por_prog_da_transf.programa_por_almoxarifado_e_transferencia_id = transf_por_almoxarifado.id
            INNER JOIN gestao_de_estoque_itens_da_transferencia itens_trans
              ON itens_trans.id = itens_por_prog_da_transf.item_da_transferencia_id
            INNER JOIN gestao_de_estoque_estoques estoque
              ON estoque.id = itens_trans.estoque_id
            INNER JOIN contabilidade_sub_elementos_de_despesa sub
              ON sub.id = estoque.sub_elemento_de_despesa_id
            INNER JOIN base_elementos_de_despesa ele
              ON ele.id = sub.elemento_de_despesa_id
            WHERE ele.codigo LIKE '____3000'
            AND sub.codigo = '07'
            AND transf.status = 1
            AND transf.id = ?
          ", transferencia_param[:id]]).first

          if transferencia_solicitada
            transfer_valida = true
            resultados_falha = []

            begin
              ActiveRecord::Base.transaction do
                transferencia_param[:escolas].each do |escola|
                  escola[:programas].each do |programa|
                    programa[:itens_por_programa_da_transferencia].each do |item|
                      item_solicitado = GestaoDeEstoque::ItemDoProgramaPorAlmoxarifadoETransferencia.find(item[:id])

                      if item_solicitado && item_solicitado.estoque_id.to_i == item[:estoque_id].to_i
                        if item_solicitado.quantidade.to_d < item[:quantidade].to_d
                          transfer_valida = false
                          resultados_falha << { item_por_programa_da_transferencia: item[:id], message: "Erro: Quantidade informada é maior que a quantidade solicitada no GERENCIAL." }
                        elsif item_solicitado.estoque.quantidade_total_saldo - item_solicitado.quantidade < 0 || item_solicitado.estoque.quantidade_total_saldo - item[:quantidade].to_d < 0
                          transfer_valida = false
                          resultados_falha << { item_por_programa_da_transferencia: item[:id], item_id: item_solicitado.item_id, descricao_item: item_solicitado.item.try(:codigo_e_descricao), message: "Erro: Quantidade informada é maior que a quantidade atual disponível no estoque." }
                        elsif item_solicitado.estoque.quantidade_total_por_data(transferencia_solicitada.data_de_transferencia) - item_solicitado.quantidade < 0 || item_solicitado.estoque.quantidade_total_por_data(transferencia_solicitada.data_de_transferencia) - item[:quantidade].to_d < 0
                          transfer_valida = false
                          resultados_falha << { item_por_programa_da_transferencia: item[:id], item_id: item_solicitado.item_id, descricao_item: item_solicitado.item.try(:codigo_e_descricao), message: "Erro: Quantidade informada é maior que a quantidade em #{transferencia_solicitada.data_de_transferencia.to_s} no estoque." }
                        end
                      else
                        transfer_valida = false
                        resultados_falha << { item_por_programa_da_transferencia: item[:id], message: "Erro: Item solicitado não encontrado ou estoque_id não corresponde." }
                      end
                    end
                  end
                end

                if transfer_valida
                  transferencia_param[:escolas].each do |escola|
                    escola[:programas].each do |programa|
                      programa[:itens_por_programa_da_transferencia].each do |item|
                        item_solicitado = GestaoDeEstoque::ItemDoProgramaPorAlmoxarifadoETransferencia.find(item[:id])
                        item_solicitado.update!(quantidade: item[:quantidade].to_d)
                      end
                    end
                  end

                  unless transferencia_solicitada.update(status: 4, confirmada_por_api: true) && transferencia_solicitada.confirmar_recebimento_da_transferencia && transferencia_solicitada.update(status: 3)
                    raise ActiveRecord::Rollback, "Erro ao atualizar status da transferência"
                  end

                  status 200
                  return { message: "Transferência confirmada com sucesso." }
                else
                  status 422
                  return { message: "Erro: Transferência não pode ser confirmada.", resultados: resultados_falha }
                end
              end
            rescue => e
              status 422
              return { message: "Erro: Ocorreu uma falha ao confirmar a transferência. Todas as alterações foram desfeitas.", error: e.message }
            end
          else
            status 422
            { message: "Erro: Transferência não encontrada." }
          end
        end
      end

      resource :listar_recebimentos_de_merenda_escolar_realizadas_por_api do
        desc 'Retorna a lista de transferências de gênero alimentar que utilizam programas escolares'
        params do
          optional :unidade_orcamentaria_origem_id, type: Integer
          optional :unidade_orcamentaria_destino_id, type: Integer
          optional :almoxarifado_origem_id, type: Integer
          optional :almoxarifado_destino_id, type: Integer
          optional :data_transferencia_inicio, type: Date
          optional :data_transferencia_fim, type: Date
          optional :page, type: Integer, default: 1
          optional :per_page, type: Integer, default: 10
        end

        get do
          page = params[:page]
          per_page = params[:per_page]

          sql = "
            SELECT DISTINCT transf.*
            FROM gestao_de_estoque_transferencias transf
            INNER JOIN gestao_de_estoque_programas_por_almoxarifado_e_transferencia transf_por_almoxarifado
              ON transf_por_almoxarifado.transferencia_id = transf.id
            INNER JOIN gestao_de_estoque_itens_do_programa_por_almoxarifado_e_transfer itens_por_prog_da_transf
              ON itens_por_prog_da_transf.programa_por_almoxarifado_e_transferencia_id = transf_por_almoxarifado.id
            INNER JOIN gestao_de_estoque_itens_da_transferencia itens_trans
              ON itens_trans.id = itens_por_prog_da_transf.item_da_transferencia_id
            INNER JOIN gestao_de_estoque_estoques estoque
              ON estoque.id = itens_trans.estoque_id
            INNER JOIN contabilidade_sub_elementos_de_despesa sub
              ON sub.id = estoque.sub_elemento_de_despesa_id
            INNER JOIN base_elementos_de_despesa ele
              ON ele.id = sub.elemento_de_despesa_id
            WHERE ele.codigo LIKE '____3000'
            AND sub.codigo = '07'
            AND transf.status = 3
            AND transf.confirmada_por_api = true
          "
          sql << " AND transf.unidade_orcamentaria_id = #{params[:unidade_orcamentaria_origem_id]}" if params[:unidade_orcamentaria_origem_id].present?
          sql << " AND transf.unidade_orcamentaria_de_destino_id = #{params[:unidade_orcamentaria_destino_id]}" if params[:unidade_orcamentaria_destino_id].present?
          sql << " AND transf.almoxarifado_id = #{params[:almoxarifado_origem_id]}" if params[:almoxarifado_origem_id].present?
          sql << " AND transf.almoxarifado_de_destino_id = #{params[:almoxarifado_destino_id]}" if params[:almoxarifado_destino_id].present?
          sql << " AND transf.data_de_transferencia >= '#{params[:data_transferencia_inicio]}'" if params[:data_transferencia_inicio].present?
          sql << " AND transf.data_de_transferencia <= '#{params[:data_transferencia_fim]}'" if params[:data_transferencia_fim].present?

          transferencias = GestaoDeEstoque::Transferencia.find_by_sql(sql).paginate(page: page, per_page: per_page)

          transferencias_escolares = transferencias.map do |transferencia|
            {
              transferencia: [
              id: transferencia.id,
              data: transferencia.data_de_transferencia,
              unidade_orcamentaria_origem_id: transferencia.try(:unidade_orcamentaria).try(:id),
              unidade_orcamentaria_origem_descricao: transferencia.try(:unidade_orcamentaria).try(:codigo_e_nome),
              almoxarifado_origem_id: transferencia.almoxarifado.id,
              almoxarifado_origem_descricao: transferencia.almoxarifado.codigo_e_nome,
              unidade_orcamentaria_destino_id: transferencia.try(:unidade_orcamentaria_de_destino).try(:id),
              unidade_orcamentaria_destino_descricao: transferencia.try(:unidade_orcamentaria_de_destino).try(:codigo_e_nome),
              almoxarifado_destino_id: transferencia.almoxarifado_de_destino.id,
              almoxarifado_destino_descricao: transferencia.almoxarifado_de_destino.codigo_e_nome,
              escolas: transferencia.programas_por_almoxarifado_e_transferencia.group_by { |t| t.programa_por_escola.escola }.map do |escola, transf_por_escola|
                {
                  id: escola.id,
                  nome: escola.nome,
                  programas: transf_por_escola.group_by { |t| t.programa_por_escola.programa }.map do |programa, transf_do_programa|
                    {
                      id: programa.id,
                      nome: programa.nome,
                      itens_por_programa_da_transferencia: transf_do_programa.flat_map { |t| t.itens_do_programa_por_almoxarifado_e_transferencia }.map do |item|
                        {
                          id: item.id,
                          item_id: item.item_id,
                          descricao: item.item.descricao,
                          quantidade: item.quantidade,
                          estoque_id: item.estoque_id
                        }
                      end
                    }
                  end
                }
              end
              ]
            }
          end
          transferencias_escolares
        end
      end

      resource :criar_consumo_de_merenda_escolar_por_api do
        desc 'Cria um consumo de merenda escolar'
        params do
          requires :data_de_consumo, type: Date
          requires :almoxarifado_id, type: Integer
          requires :unidade_orcamentaria_id, type: Integer
          requires :escola_id, type: Integer
          optional :historico, type: String
          requires :programas_por_consumo, type: Array do
            requires :id, type: Integer
            requires :itens_do_consumo_por_programa, type: Array do
              requires :item_id, type: Integer
              requires :quantidade, type: Float
            end
          end
        end

        post do
          ActiveRecord::Base.transaction do
            consumo = GestaoDeEstoque::Consumo.new(
              data_de_consumo: params[:data_de_consumo],
              almoxarifado_id: params[:almoxarifado_id],
              unidade_orcamentaria_id: params[:unidade_orcamentaria_id],
              escola_id: params[:escola_id],
              historico: params[:historico],
              status: 3,
              orcamento_id: Orcamento.find_by(exercicio: params[:data_de_consumo].year)&.id,
              usa_escola: true,
              gerado_por_api: true
            )


            params[:programas_por_consumo].each do |programa|
              programa_por_escola = ProgramaPorEscola.find_by(escola_id: params[:escola_id], id: programa[:id])
              error!("Programa por escola não encontrado para o programa_id: #{programa[:id]}.", 422) unless programa_por_escola

              programa_por_consumo = consumo.programas_por_consumo.build(
                escola_id: params[:escola_id],
                programa_por_escola_id: programa_por_escola.id
              )

              programa[:itens_do_consumo_por_programa].each do |item|
                estoque = GestaoDeEstoque::Estoque.find_by(almoxarifado_id: consumo.almoxarifado_id, unidade_orcamentaria_id: consumo.unidade_orcamentaria_id, item_id: item[:item_id])
                error!("Estoque não encontrado para o item_id #{item[:item_id]}.", 422) unless estoque

                programa_por_consumo.itens_do_consumo_por_programa.build(
                  estoque_id: estoque.id,
                  quantidade: item[:quantidade]
                )
              end
            end

            if consumo.valid? && !consumo.itens_do_consumo.present?
              consumo.programas_por_consumo.flat_map{|prog| prog.itens_do_consumo_por_programa}.group_by(&:estoque_id).each do |estoque_id, itens|
                total_quantidade = itens.sum(&:quantidade)
                estoque = GestaoDeEstoque::Estoque.find(estoque_id)

                error!("Estoque com ID #{estoque_id} não possui saldo sufuciente. Saldo atual: #{estoque.quantidade_total_saldo.to_f}. Quantidade total solicitada: #{total_quantidade}", 422) unless estoque.quantidade_total_saldo.to_f >= total_quantidade
              end
            end

            if consumo.save
              status 201
              { message: "Consumo criado com sucesso.", id: consumo.id }
            else
              error!({ message: consumo.errors.full_messages.to_sentence }, 422)
            end
          rescue StandardError => e
            status 422
            error!({ message: e.message }, 422)
          end
        end
      end

      resource :listar_consumos_da_merenda_escolar_feitos_por_api do
        desc 'Retorna a lista dos consumos de merenda feitos por requisições de outros sistemas'
        params do
          requires :ano_do_almoxarifado, type: Integer, desc: 'Ano do almoxarifado'
          optional :unidade_orcamentaria_id, type: Integer
          optional :almoxarifado_id, type: Integer
          optional :data_inicio, type: Date
          optional :data_fim, type: Date
          optional :page, type: Integer, default: 1
          optional :per_page, type: Integer, default: 10
        end

        get do
          ano = params[:ano_do_almoxarifado]

          unless ano && ano > 0
            error!('Ano do almoxarifado inválido', 400)
          end

          orcamento = Orcamento.find_by(exercicio: ano)

          unless orcamento
            error!('Orçamento não encontrado para o ano fornecido', 404)
          end

          sql = "
            SELECT DISTINCT consumo.*
            FROM gestao_de_estoque_consumos consumo
            INNER JOIN gestao_de_estoque_almoxarifados almoxarifado ON almoxarifado.id = consumo.almoxarifado_id
            INNER JOIN gestao_de_estoque_programas_por_consumo programa_por_consumo ON consumo.id = programa_por_consumo.consumo_id
            WHERE almoxarifado.orcamento_id = #{orcamento.id}
            AND consumo.status = 3
            AND consumo.gerado_por_api = TRUE
          "
          sql << " AND consumo.unidade_orcamentaria_id = #{params[:unidade_orcamentaria_id]}" if params[:unidade_orcamentaria_id].present?
          sql << " AND consumo.almoxarifado_id = #{params[:almoxarifado_id]}" if params[:almoxarifado_id].present?
          sql << " AND consumo.data_de_consumo >= '#{params[:data_inicio]}'" if params[:data_inicio].present?
          sql << " AND consumo.data_de_consumo <= '#{params[:data_fim]}'" if params[:data_fim].present?

          consumos = GestaoDeEstoque::Consumo.find_by_sql(sql).paginate(page: params[:page], per_page: params[:per_page])

          consumos_with_items = consumos.map do |consumo|
            programas_por_consumo = consumo.programas_por_consumo.group_by(&:escola)

            {
              consumo: {
                id: consumo.id,
                codigo: consumo.codigo,
                data: consumo.data_de_consumo,
                descricao: consumo.historico,
                unidade_orcamentaria_id: consumo.try(:unidade_orcamentaria).try(:id),
                unidade_orcamentaria_descricao: consumo.try(:unidade_orcamentaria).try(:codigo_e_nome),
                almoxarifado_id: consumo.try(:almoxarifado).try(:id),
                almoxarifado_descricao: consumo.try(:almoxarifado).try(:codigo_e_nome),
                programas_por_consumo: programas_por_consumo.map do |escola, epcs|
                  {
                    escola: {
                      id: escola.id,
                      nome: escola.nome,
                      programas: epcs.group_by(&:programa_por_escola).map do |programa_por_escola, consumos_por_programa_da_escola|
                        {
                          id: programa_por_escola.programa.id,
                          nome: programa_por_escola.programa.nome,
                          itens_do_consumo_por_programa: consumos_por_programa_da_escola.flat_map(&:itens_do_consumo_por_programa).map do |item|
                            {
                              id: item.id,
                              item_id: item.item_do_consumo.item_id,
                              descricao: item.item_do_consumo.item.descricao,
                              quantidade: item.quantidade.to_f
                            }
                          end
                        }
                      end
                    }
                  }
                end
              }
            }
          end

          present consumos_with_items
        end
      end

      resource :retorna_saldo_do_estoque_por_almoxarifado do
        desc 'Retorna saldo do estoque por almoxarifado'
        params do
          requires :almoxarifado_id, type: Integer, desc: 'ID do almoxarifado'
          requires :unidade_orcamentaria_id, type: Integer, desc: 'ID da unidade orcamentaria'
          optional :item_id, type: Integer, desc: 'ID do item para filtro'
          optional :page, type: Integer, desc: 'Número da página'
          optional :per_page, type: Integer, desc: 'Itens por página'
        end

        get do
          almoxarifado = GestaoDeEstoque::Almoxarifado.find_by(id: params[:almoxarifado_id])
          unidade_orcamentaria = Loa::UnidadeOrcamentaria.find_by(id: params[:unidade_orcamentaria_id])

          if almoxarifado.present? && unidade_orcamentaria.present?
            estoques = GestaoDeEstoque::Estoque.where(almoxarifado_id: almoxarifado.id , unidade_orcamentaria_id: params[:unidade_orcamentaria_id])

            estoques = estoques.where(item_id: params[:item_id]) if params[:item_id].present?

            page = params[:page] || 1
            per_page = params[:per_page] || 10
            estoques = estoques.paginate(page: page, per_page: per_page)
            response = estoques.sort_by(&:item).map do |estoque|
              {
                id: estoque.id,
                item_id: estoque.item_id,
                item_descricao: estoque.item.try(:codigo_e_descricao),
                programa: estoque.programa_por_escola.present? ? estoque.programa_por_escola.nome_programa_e_acao_completa : nil,
                unidade_de_medida: estoque.unidade_de_medida.descricao,
                entrada: estoque.quantidade_total_entradas.to_f.valor_contabil(minimum_precision: 4),
                saida: estoque.quantidade_total_saidas.to_f.valor_contabil(minimum_precision: 4),
                saldo: estoque.quantidade_total_saldo.to_f.valor_contabil(minimum_precision: 4)
              }
            end
            present response
          else
            error!('Almoxarifado ou Unidade Orçamentária não encontrados', 404)
          end
        end
      end

      # resource :remove_confirmacao_do_consumo_da_merenda_escolar do
      #   desc 'Remove confirmação do consumo da merenda escolar realizado pelo outro sistema'
      #   params do
      #     requires :consumos_ids, type: Array[Integer], desc: 'IDs dos consumos a serem confirmados'
      #   end
      #   post do
      #     consumos_validos = GestaoDeEstoque::Consumo.joins(:almoxarifado)
      #     .where(gestao_de_estoque_almoxarifados: { orcamento_id: Orcamento.find_by_exercicio(Date.today.year).id, codigo: '019' })
      #     .where(status: 3, consumido_por_api: true)
      #     .where(id: params[:consumos_ids])

      #     consumos_invalidos = params[:consumos_ids] - consumos_validos.pluck(:id)

      #     if consumos_validos.present?
      #       unless consumos_invalidos.present?
      #         if consumos_validos.update_all(consumido_por_api: false)
      #           status 200
      #           { message: 'Todos os consumos foram revertidos com sucesso' }
      #         else
      #           status 422
      #           { message: 'Falha ao reverter os consumos informados' }
      #         end
      #       else
      #         if consumos_validos.update_all(consumido_por_api: false)
      #           status 207
      #           {
      #             message: 'Reverção parcial. Alguns consumos não foram revertidos porque não foi confirmado pela API ou, não estão com o status confirmado, ou não estão na lista da merenda escolar no sistema gerencial.',
      #             revertidos_com_sucesso: {consumos_ids: consumos_validos.pluck(:id)},
      #             nao_revertidos: {consumos_ids: consumos_invalidos}
      #           }
      #         else
      #           status 422
      #           { message: 'Falha ao reverter os consumos válidos' }
      #         end
      #       end
      #     else
      #       status 422
      #       { message: 'Nenhum consumo válido encontrado para ser revertido' }
      #     end
      #   end
      # end
    end
  end
end
