class Loa::UnidadeOrcamentaria < ApplicationRecord
	include IncrementadorDeCodigoConcern

	has_paper_trail
	has_attached_file :logo

	validates_attachment_content_type :logo, :content_type => ["image/jpg", "image/jpeg", "image/png"]

	after_initialize :sugestao_de_codigo, if: :new_record?

	attr_accessor :skip_validacao

	attr_default :importar_dependencias, true
	attr_default :is_unidade_arrecadadora, false
	attr_default :rpps, false
	attr_default :saae, false

	belongs_to :orgao, required: true
	belongs_to :unidade_gestora, required: true
	belongs_to :tipo_de_administracao, class_name: 'Base::TipoDeAdministracao', required: true
	belongs_to :tipo_de_unidade_administrativa, class_name: 'Base::TipoDeUnidadeAdministrativa', required: true
	belongs_to :ppa_unidade_orcamentaria, class_name: 'Ppa::UnidadeOrcamentaria', foreign_key: 'ppa_unidade_orcamentaria_id'
	belongs_to :pessoa, class_name: 'Base::Pessoa'
	belongs_to :assessor_de_contabilidade_responsavel, foreign_key: 'assessor_contabilidade_responsavel_id',  class_name: 'Loa::AssessorDeContabilidadeResponsavel'

	has_many :unidades_orcamentarias_por_etp, class_name: 'Licitacao::UnidadeOrcamentariaPorEtp'
	has_many :unidades_orcamentarias_por_solucao_do_etp, class_name: 'Licitacao::UnidadeOrcamentariaPorSolucaoDoEtp'
	has_many :solucoes_do_etp, through: :unidades_orcamentarias_por_solucoes_do_etp, class_name: 'Licitacao::SolucoesDoEtp', dependent: :restrict_with_exception
	has_many :unidades_orcamentarias_por_usuario, class_name: 'Loa::UnidadeOrcamentariaPorUsuario'
	has_many :usuarios, through: :unidades_orcamentarias_por_usuario, class_name: 'Usuario'
	has_many :unidades_orcamentaria_vinculada, class_name: 'Loa::UnidadeOrcamentariaVinculada', inverse_of: :unidade_orcamentaria, dependent: :destroy
	has_many :agente_publico, class_name: 'Base::AgentePublicoMunicipal'
	has_many :contas_extra_por_unidades_orcamentarias, class_name: 'Contabilidade::ContaExtraPorUnidadeOrcamentaria'
	has_many :contas_extra_orcamentarias, through: :contas_extra_por_unidades_orcamentarias, source: :conta_extra_orcamentaria, class_name: 'Contabilidade::ContaExtraOrcamentaria'

	has_many :subacoes, dependent: :restrict_with_exception
	has_many :acoes, through: :subacoes
	has_many :programas_de_governo, through: :acoes
	has_many :elementos_de_despesa_por_subacao, through: :subacoes, class_name: 'Loa::ElementoDeDespesaPorSubacao'
	has_many :elementos_de_despesa, through: :elementos_de_despesa_por_subacao, class_name: 'Base::ElementoDeDespesa'
	has_many :orcamentos_da_despesa, through: :elementos_de_despesa_por_subacao, class_name:'Loa::OrcamentoDaDespesa'
	has_many :empenhos, through: :orcamentos_da_despesa, class_name: 'Contabilidade::Empenho'
	has_many :liquidacoes, through: :empenhos, class_name: 'Contabilidade::Liquidacao'
	has_many :pagamentos, through: :liquidacoes, class_name: 'Contabilidade::Pagamento'
	has_many :ordens_de_compra, through: :empenhos, class_name: 'Licitacao::OrdemDeCompra'
	has_many :unidades_orcamentarias_do_almoxarifado, dependent: :destroy
	has_many :despesas_extra_orcamentarias, class_name: 'Contabilidade::DespesaExtraOrcamentaria'
	has_many :taloes_de_receita, class_name: 'Contabilidade::TalaoDeReceita'
	has_many :anulacoes_dos_taloes_de_receita, through: :taloes_de_receita, class_name: 'Contabilidade::AnulacaoDoTalaoDeReceita'

	has_many :ordenadores_de_despesa, class_name: 'Loa::OrdenadorDeDespesa' do
		def atual
			find_by('(data_inicio_da_gestao <=  ? AND data_fim_de_gestao >= ?) OR (data_inicio_da_gestao <=  ? AND data_fim_de_gestao IS NULL)', Date.today, Date.today, Date.today)
		end

		def atual_na_data(data)
			find_by('(data_inicio_da_gestao <=  ? AND data_fim_de_gestao >= ?) OR (data_inicio_da_gestao <=  ? AND data_fim_de_gestao IS NULL)', data, data, data)
		end
	end

	has_many :subfuncoes, through: :subacoes, class_name: 'Base::Subfuncao'
	has_many :funcoes, through: :subfuncoes, class_name: 'Base::Funcao'
	has_many :fontes_de_recursos, through: :orcamentos_da_despesa, class_name: "Base::FonteDeRecursos"
	has_many :modalidades_de_aplicacao, through: :elementos_de_despesa, class_name: 'Base::ModalidadeDeAplicacao'
	has_many :grupos_de_natureza_da_despesa, through: :modalidades_de_aplicacao, class_name: 'Base::GrupoDeNaturezaDaDespesa'

	has_many :unidades_orcamentarias_por_natureza_da_receita, dependent: :restrict_with_exception
	has_many :naturezas_da_receita, through: :unidades_orcamentarias_por_natureza_da_receita
	has_many :orcamentos_da_receita, through: :unidades_orcamentarias_por_natureza_da_receita
	has_many :fontes_de_recursos_da_receita, through: :orcamentos_da_receita, source: :fonte_de_recursos, class_name: "Base::FonteDeRecursos"
	has_many :transferencias_de_recursos_origem, class_name: 'Loa::TransferenciaDeRecurso', foreign_key: "unidade_orcamentaria_origem_id"
	has_many :transferencias_de_recursos_destino, class_name: 'Loa::TransferenciaDeRecurso', foreign_key: "unidade_orcamentaria_destino_id"

	has_many :contas_bancarias_por_unidade_orcamentaria, class_name: 'Base::ContaBancariaPorUnidadeOrcamentaria', dependent: :destroy
	has_many :contas_bancarias, class_name: 'Base::ContaBancaria', through: :contas_bancarias_por_unidade_orcamentaria

	has_many :unidades_orcamentarias_por_pedido, class_name: 'Licitacao::UnidadeOrcamentariaPorPedido'
	has_many :pedidos, through: :unidades_orcamentarias_por_pedido, class_name: 'Licitacao::Pedido', dependent: :restrict_with_exception
	has_many :projetos, through: :pedidos, class_name: 'Licitacao::Projeto', dependent: :restrict_with_exception
	has_many :processos, through: :pedidos, class_name: 'Licitacao::Processo'
	has_many :contratos, class_name: "Licitacao::Contrato", foreign_key: :unidade_orcamentaria_do_exercicio_id, dependent: :restrict_with_exception
	has_many :passagens, class_name: "Contabilidade::Passagem", dependent: :restrict_with_exception

	has_many :contas_por_unidades_orcamentarias, dependent: :destroy, class_name: "Contabilidade::ContaPorUnidadeOrcamentaria"
	has_many :contas, through: :contas_por_unidades_orcamentarias, class_name: "Contabilidade::Conta"

	has_many :convenios, class_name: "Contabilidade::Convenio", inverse_of: :unidade_orcamentaria
	has_many :diarias, class_name: "Contabilidade::Diaria", inverse_of: :unidade_orcamentaria

	has_many :unidades_orcamentarias_do_almoxarifado, class_name: "GestaoDeEstoque::UnidadeOrcamentariaDoAlmoxarifado"
	has_many :recebimento_de_materiais, class_name: "GestaoDeEstoque::RecebimentoDeMaterial"
	has_many :requisicoes_de_materiais, class_name: "Administrativo::RequisicaoDeMaterial"
	has_many :movimentacoes_do_estoque, class_name: "GestaoDeEstoque::MovimentacaoDoEstoque"

	has_one :orcamento, through: :orgao

	accepts_nested_attributes_for :unidades_orcamentaria_vinculada, reject_if: :all_blank, allow_destroy: true
	validates_associated :unidades_orcamentaria_vinculada

	validates_presence_of :unidade_gestora_id, :orgao_id, :nome, :sigla, :codigo, :tipo_de_administracao_id, :tipo_de_unidade_administrativa_id, :status_do_orcamento, :data_de_inclusao
	validates_uniqueness_of :nome, :sigla, :codigo, scope: [:unidade_gestora_id, :orgao_id], case_sensitive: false
	validates_presence_of :situacao_orcamentaria, if: :rpps
	validates_inclusion_of :parcelamento, :in => [true, false], if: :rpps

	validates_length_of :nome, maximum: 80
	validates_length_of :codigo, maximum: 4
	validates_length_of :sigla, maximum: 10

	validate :uniqueness_unidade_reserva_de_contingencia
	validate :prefeitura_deve_ser_salva_no_orgao_correto
	validate :codigo_de_prefeitura_deve_ser_AAA, unless: :skip_validacao
	validate :deve_ter_o_mesmo_tipo_de_poder_do_orgao
	validate :saae_deve_ter_sempre_tipo_de_administracao_como_autarquia

	validates :unidades_orcamentaria_vinculada, uniq_nested_attributes: { atributo: :unidade_orcamentaria_vinculada_id, mensagem: "unidade deve ser única dentro do vínculo" }

	before_save :upcase_sigla
	before_save :atualiza_situacao_se_nao_for_rpps
	before_save :cria_pessoa_associada, if: Proc.new{ pessoa_id.nil? }

	after_create	:adiciona_unidades_aos_usuarios

	enum status_do_orcamento: STATUS_DO_ORCAMENTO

	enum situacao_orcamentaria: {
		"Superavitária": 0,
		"Deficitária": 1
	}

	scope :por_tipo_de_sistema, -> {
		if Configuracao.try(:first).try(:camara_municipal?)
			self.joins(:tipo_de_unidade_administrativa).where("base_tipos_de_unidades_administrativas.codigo = '02'")
		else
			self.joins(:tipo_de_unidade_administrativa).where("base_tipos_de_unidades_administrativas.codigo != '02'")
		end
	}

	scope :exceto_reserva_de_contingencia, -> {
		joins(:tipo_de_unidade_administrativa).where.not(base_tipos_de_unidades_administrativas: {codigo: '99'})
	}

	scope :ordenado_por_orgao, -> { joins(:orgao).order('loa_orgaos.codigo, loa_unidades_orcamentarias.codigo') }

	scope :nao_revisados, -> { joins(:unidades_orcamentarias_por_natureza_da_receita).where("loa_unidades_orcamentarias_por_natureza_da_receita.forma_de_adicao = '1' or loa_unidades_orcamentarias_por_natureza_da_receita.forma_de_adicao is null") }

	def codigo_e_nome
		"#{codigo_completo} - #{nome}"
	end

	def codigo_e_nome_e_exercicio
		"#{codigo_e_nome} - #{orgao.orcamento.exercicio}"
	end

	def nome_e_exercicio
		"#{nome} - #{orgao.orcamento.exercicio}"
	end

	def codigo_completo
		self.orgao.codigo + self.codigo_formatado
	end

	def codigo_completo_e_sigla
		"#{codigo_completo} - #{sigla}"
	end

	def codigo_completo_e_nome
		"#{codigo_completo} - #{nome}"
	end

	def codigo_completo_e_nome_e_exercicio
		"#{codigo_completo} - #{nome} - #{orgao.orcamento.exercicio}"
	end

	def codigo_formatado
		"#{codigo.rjust(2, '0')}"
	end

	def exercicio
		orgao.orcamento&.exercicio.to_i
	end

	def valor_total
		orcamentos_da_receita.sum(:valor)
	end

	def valor_total_fixado_da_despesa
		orcamentos_da_despesa.sum(:valor)
	end

	def possui_unidade_gestora?
		unidade_gestora.present?
	end

	def possui_lancamentos_na_receita?
		orcamentos_da_receita.present?
	end

	def reserva_de_contingencia?
		tipo_de_unidade_administrativa.try(:codigo) == '99'
	end

	def to_sim
		begin
			texto = ""
			texto << "104".to_s.sim_limite(3) + ","
			texto << Configuracao.first.codigo_do_municipio_no_tcm.to_s.sim_preenche(3) + ","
			texto << orgao.orcamento.exercicio.to_s.sim_limite_sem_aspas(4, "00") + ","
			texto << orgao.codigo.to_s.sim_preenche(2) + ","
			texto << codigo.to_s.sim_limite(4) + ","
			texto << tipo_de_unidade_administrativa.codigo.to_s.sim_limite(2) + ","
			texto << nome.to_s.upcase.sim_limite(80) + ","
			texto << tipo_de_administracao.sigla.to_s.sim_limite(1)
			return texto
		rescue => e
			if e.class.to_s == "NoMethodError"
				atributo_falho = e.message.split(" ")[2]
				coluna = CSV.parse(texto, :headers => false).last.count
				raise e.mensagem_traduzida(self, self.codigo, atributo_falho, coluna)
			else
				raise e
			end
		end
	end

	def programas_de_governo
		orgao.orcamento.programas_de_governo.joins(acoes: :subacoes).where(loa_subacoes: {unidade_orcamentaria_id: self.id}).distinct
	end

	def total_da_receita_por_fonte_de_recurso(fonte_de_recursos_id)
		(self.orcamentos_da_receita.where(fonte_de_recursos_id: fonte_de_recursos_id).sum(:valor) + self.transferencias_de_recursos_destino.where(fonte_de_recursos_id: fonte_de_recursos_id).sum(:valor) - self.transferencias_de_recursos_origem.where(fonte_de_recursos_id: fonte_de_recursos_id).sum(:valor)).to_f
	end

	def total_da_despesa_por_fonte_de_recurso(fonte_de_recursos_id)
		self.subacoes.inject(0) {|total, subacao| total += subacao.valor_total_fixado_por_fonte_de_recursos(fonte_de_recursos_id)}
	end

	def diferenca_entre_receita_e_despesa_por_fonte_de_recurso(fonte_de_recursos_id)
		(self.total_da_receita_por_fonte_de_recurso(fonte_de_recursos_id) - self.total_da_despesa_por_fonte_de_recurso(fonte_de_recursos_id)).to_f
	end

	def ordenadores_de_despesas_ativos(data_atual)
		self.ordenadores_de_despesa.where("data_inicio_da_gestao <= ? and (data_fim_de_gestao >= ? or data_fim_de_gestao is null)", data_atual, data_atual).all
	end

	def ordenador_de_despesa_na_data(data)
		self.ordenadores_de_despesa.where("data_inicio_da_gestao <= ? and (data_fim_de_gestao >= ? or data_fim_de_gestao is null)", data, data).last
	end

	def valida_gestor_da_unidade
		unless unidade_gestora.gestores.atual.present?
			# exercicio = unidade_gestora.orcamento.exercicio.to_s
			raise "Não existe gestor ativo para a Unidade Gestora: " << "#{unidade_gestora.nome}."
		end
	end

	def unidades_orcamentarias_de_exercicios_anteriores_ids
		cod_orgao = self.orgao.codigo
		cod_unidade = self.codigo
		unidades_orcamentarias = Loa::UnidadeOrcamentaria.joins(:orgao).where(codigo: cod_unidade, loa_orgaos: {codigo: cod_orgao}).pluck(:id)
		if unidades_orcamentarias.blank?
			unidades_orcamentarias = orcamento.unidades_orcamentarias.joins(:unidades_orcamentaria_vinculada).find_by('loa_unidades_orcamentaria_vinculada.unidade_orcamentaria_vinculada_id = ?', self.id).try(:id)
		end
		return unidades_orcamentarias
	end

	def resgata_ordenador_antigo
		ordenadores_de_despesa = Loa::OrdenadorDeDespesa.joins(:unidade_orcamentaria, :orcamento).where("loa_unidades_orcamentarias.sigla = ? AND orcamentos.exercicio = ?" , self.sigla, self.orgao.orcamento.exercicio - 1).all
		ordenadores_de_despesa.each do |ordenador_de_despesa|
			ordenadores_de_despesa.build(agente_publico_id: ordenador_de_despesa.agente_publico_id, data_inicio_da_gestao: ordenador_de_despesa.data_inicio_da_gestao, unidade_orcamentaria_id: self.id).save(validate: false)
		end
	end

	def cria_pessoa_associada
		if orgao.present?
			tipo_de_pessoa = Base::TipoDePessoa.find_by(codigo: '2')
			pessoa_unidade_orcamentaria = Base::Pessoa.find_or_initialize_by(
				cnpj: orgao.cnpj,
				nome: nome,
				tipo_de_pessoa_id: tipo_de_pessoa.id,
				data_do_cadastro: Date.today,
				cep: orgao.cep,
				email: orgao.email,
				logradouro: orgao.endereco,
				porte: 'outros',
				cidade_id: Configuracao.last.cidade_id
			)

			pessoa_unidade_orcamentaria.save!(validate: false)

			self.pessoa_id =  pessoa_unidade_orcamentaria.id
		end
	end

	def transferencias_financeiras_origem
		Contabilidade::TransferenciaFinanceira.joins(:conta_bancaria_origem).where(base_contas_bancarias_por_unidade_orcamentaria: {unidade_orcamentaria_id: id})
	end

	def transferencias_financeiras_destino
		Contabilidade::TransferenciaFinanceira.joins(:conta_bancaria_destino).where(base_contas_bancarias_por_unidade_orcamentaria: {unidade_orcamentaria_id: id})
	end

	def adiciona_unidades_aos_usuarios
		Usuario.where(nivel_de_permissao_a_unidades: 2, aprovado: true).each do |usuario|
			Loa::UnidadeOrcamentariaPorUsuario.find_or_create_by(unidade_orcamentaria_id: id, usuario_id: usuario.id) rescue nil
		end
	end

	private
	def sugestao_de_codigo
		gerar_sugestao_codigo( :codigo, 3, {orgao_id: orgao.id} ) if (orgao.present? && codigo.blank?)
	end

	def upcase_sigla
		self.sigla.try(:upcase!)
	end

	def uniqueness_unidade_reserva_de_contingencia
		if self.try(:tipo_de_unidade_administrativa).try(:codigo) == '99'
			unidade_cadastrada = ::Loa::UnidadeOrcamentaria.find_by( orgao_id: orgao.id, tipo_de_unidade_administrativa_id: tipo_de_unidade_administrativa.id )
			errors.add(:tipo_de_unidade_administrativa_id, "já existe uma unidade orçamentária do tipo reserva de contingência") if unidade_cadastrada.present?
		end
	end

	def prefeitura_deve_ser_salva_no_orgao_correto
		if self.try(:tipo_de_unidade_administrativa).try(:codigo) == '00' && self.orgao.codigo != 'AA'
			errors.add(:tipo_de_unidade_administrativa_id, "Não é possível cadastrar uma prefeitura para esse órgão")
		end
	end

	def codigo_de_prefeitura_deve_ser_AAA
		if self.try(:tipo_de_unidade_administrativa).try(:codigo) == '00' && self.codigo != 'AAA'
			errors.add(:codigo, "Esse código não corresponde ao de uma prefeitura")
		end
	end

	def deve_ter_o_mesmo_tipo_de_poder_do_orgao
		if self.orgao.present? && self.tipo_de_unidade_administrativa_id.present?
			poder_do_orgao = self.orgao.tipo_de_unidade_administrativa.poder_associado
			poder_da_nova_unidade = Base::TipoDeUnidadeAdministrativa.find(self.tipo_de_unidade_administrativa_id).poder_associado

			if poder_do_orgao != poder_da_nova_unidade
				errors.add(:tipo_de_unidade_administrativa_id, "Não é possivel escolher um tipo de unidade administrativa com poder diferente ao escolhido no orgão")
			end
		end
	end

	def atualiza_situacao_se_nao_for_rpps
		unless self.rpps
			self.parcelamento = false
			self.situacao_orcamentaria = nil
		end
	end

	def saae_deve_ter_sempre_tipo_de_administracao_como_autarquia
		if saae && tipo_de_unidade_administrativa.codigo != '05'
			errors.add(:tipo_de_unidade_administrativa_id, "SAAE deve ter o tipo de unidade administrativa Autarquia")
		end
	end
end
