package br.gov.sigefp.budget.service;

import br.gov.sigefp.budget.api.dto.FiscalYearDTO;
import br.gov.sigefp.budget.domain.BudgetExecution;
import br.gov.sigefp.budget.domain.FiscalYear;
import br.gov.sigefp.budget.repository.BudgetExecutionRepository;
import br.gov.sigefp.budget.repository.FiscalYearRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * Serviço de aplicação para gestão de anos fiscais.
 */
@Service
@RequiredArgsConstructor
@Transactional
public class FiscalYearService {

    private final FiscalYearRepository fiscalYearRepository;
    private final BudgetExecutionRepository budgetExecutionRepository;

    public FiscalYearDTO create(FiscalYearDTO dto) {
        if (fiscalYearRepository.existsByYear(dto.getYear())) {
            throw new IllegalArgumentException("Ano fiscal já existe: " + dto.getYear());
        }

        FiscalYear fiscalYear = FiscalYear.builder()
                .year(dto.getYear())
                .startDate(dto.getStartDate())
                .endDate(dto.getEndDate())
                .status(dto.getStatus() != null ? dto.getStatus() : "DRAFT")
                .build();

        FiscalYear saved = fiscalYearRepository.save(fiscalYear);
        return toDTO(saved);
    }

    public FiscalYearDTO open(UUID id) {
        FiscalYear fiscalYear = fiscalYearRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Ano fiscal não encontrado: " + id));

        fiscalYear.setStatus("OPEN");
        FiscalYear saved = fiscalYearRepository.save(fiscalYear);
        return toDTO(saved);
    }

    public FiscalYearDTO close(UUID id) {
        FiscalYear fiscalYear = fiscalYearRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Ano fiscal não encontrado: " + id));

        // Validação: não permitir fechar se existirem movimentos em aberto
        // Verificar se há execuções pendentes (COMMITMENT) de forma performática
        // (COUNT)
        long pendingCommitments = budgetExecutionRepository.countByFiscalYearIdAndMovementType(id, "COMMITMENT");

        if (pendingCommitments > 0) {
            throw new IllegalArgumentException("Não é possível fechar o ano fiscal: existem movimentos em aberto");
        }

        fiscalYear.setStatus("CLOSED");
        FiscalYear saved = fiscalYearRepository.save(fiscalYear);
        return toDTO(saved);
    }

    @Transactional(readOnly = true)
    public FiscalYearDTO findById(UUID id) {
        FiscalYear fiscalYear = fiscalYearRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Ano fiscal não encontrado: " + id));
        return toDTO(fiscalYear);
    }

    @Transactional(readOnly = true)
    public Page<FiscalYearDTO> findAll(Pageable pageable) {
        return fiscalYearRepository.findAll(pageable).map(this::toDTO);
    }

    @Transactional(readOnly = true)
    public FiscalYearDTO getCurrentFiscalYear() {
        FiscalYear fiscalYear = fiscalYearRepository.findCurrentFiscalYear()
                .orElseThrow(() -> new IllegalArgumentException("Não existe ano fiscal aberto"));
        return toDTO(fiscalYear);
    }

    private FiscalYearDTO toDTO(FiscalYear fiscalYear) {
        return FiscalYearDTO.builder()
                .id(fiscalYear.getId())
                .year(fiscalYear.getYear())
                .startDate(fiscalYear.getStartDate())
                .endDate(fiscalYear.getEndDate())
                .status(fiscalYear.getStatus())
                .build();
    }
}
