package br.gov.sigefp.rh.service;

import br.gov.sigefp.rh.api.dto.*;
import br.gov.sigefp.rh.domain.SalaryCategory;
import br.gov.sigefp.rh.domain.SalaryGrade;
import br.gov.sigefp.rh.domain.SalaryGrid;
import br.gov.sigefp.rh.domain.SalaryStep;
import br.gov.sigefp.rh.repository.AgentRepository;
import br.gov.sigefp.rh.repository.SalaryCategoryRepository;
import br.gov.sigefp.rh.repository.SalaryGradeRepository;
import br.gov.sigefp.rh.repository.SalaryGridRepository;
import br.gov.sigefp.rh.repository.SalaryStepRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Transactional
public class SalaryStructureService {

    private final SalaryCategoryRepository categoryRepository;
    private final SalaryGradeRepository gradeRepository;
    private final SalaryStepRepository stepRepository;
    private final SalaryGridRepository gridRepository;
    private final AgentRepository agentRepository;
    private final br.gov.sigefp.rh.repository.CareerRegimeRepository careerRegimeRepository;

    public SalaryStructureFullDTO getFullStructure() {
        List<SalaryCategory> categories = categoryRepository.findAll();
        LocalDate today = LocalDate.now();

        List<SalaryStructureFullDTO.CategoryGroupDTO> categoryDTOs = categories.stream().map(cat -> {
            List<SalaryGrade> grades = gradeRepository.findByCategoryId(cat.getId());

            List<SalaryStructureFullDTO.GradeRowDTO> gradeDTOs = grades.stream().map(grade -> {
                List<SalaryStructureFullDTO.StepDetailDTO> stepDTOs = grade.getSteps().stream()
                        .sorted((a, b) -> a.getStepNumber() - b.getStepNumber())
                        .map(step -> {
                            // Find the grid entry valid for today
                            SalaryGrid activeGrid = step.getGrids().stream()
                                    .filter(g -> !today.isBefore(g.getValidFrom()) &&
                                            (g.getValidTo() == null || !today.isAfter(g.getValidTo())))
                                    .findFirst()
                                    .orElse(null);

                            return SalaryStructureFullDTO.StepDetailDTO.builder()
                                    .id(step.getId())
                                    .stepNumber(step.getStepNumber())
                                    .currentValue(activeGrid != null ? activeGrid.getBaseAmount().doubleValue() : null)
                                    .subsidyAmount(activeGrid != null && activeGrid.getSubsidyAmount() != null
                                            ? activeGrid.getSubsidyAmount().doubleValue()
                                            : null)
                                    .grossAmount(activeGrid != null && activeGrid.getGrossAmount() != null
                                            ? activeGrid.getGrossAmount().doubleValue()
                                            : null)
                                    .build();
                        }).collect(Collectors.toList());

                return SalaryStructureFullDTO.GradeRowDTO.builder()
                        .id(grade.getId())
                        .code(grade.getCode())
                        .name(grade.getName())
                        .steps(stepDTOs)
                        .build();
            }).collect(Collectors.toList());

            return SalaryStructureFullDTO.CategoryGroupDTO.builder()
                    .id(cat.getId())
                    .code(cat.getCode())
                    .name(cat.getName())
                    .regimeName(cat.getRegime() != null ? cat.getRegime().getName() : null)
                    .grades(gradeDTOs)
                    .build();
        }).collect(Collectors.toList());

        return SalaryStructureFullDTO.builder().categories(categoryDTOs).build();
    }

    // --- Category methods ---
    public List<SalaryCategoryDTO> getAllCategories() {
        return categoryRepository.findAll().stream()
                .map(this::toCategoryDTO)
                .collect(Collectors.toList());
    }

    public SalaryCategoryDTO createCategory(SalaryCategoryDTO dto) {
        if (categoryRepository.existsByCode(dto.getCode())) {
            throw new IllegalArgumentException("Categoria com código " + dto.getCode() + " já existe");
        }
        SalaryCategory entity = new SalaryCategory();
        entity.setCode(dto.getCode());
        entity.setName(dto.getName());

        if (dto.getRegimeId() != null) {
            br.gov.sigefp.rh.domain.CareerRegime regime = careerRegimeRepository.findById(dto.getRegimeId())
                    .orElseThrow(() -> new IllegalArgumentException("Regime não encontrado"));
            entity.setRegime(regime);
        }

        return toCategoryDTO(categoryRepository.save(entity));
    }

    public SalaryCategoryDTO updateCategory(UUID id, SalaryCategoryDTO dto) {
        SalaryCategory category = categoryRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Categoria não encontrada"));

        if (!category.getCode().equals(dto.getCode()) && categoryRepository.existsByCode(dto.getCode())) {
            throw new IllegalArgumentException("Categoria com código " + dto.getCode() + " já existe");
        }

        category.setCode(dto.getCode());
        category.setName(dto.getName());

        if (dto.getRegimeId() != null) {
            br.gov.sigefp.rh.domain.CareerRegime regime = careerRegimeRepository.findById(dto.getRegimeId())
                    .orElseThrow(() -> new IllegalArgumentException("Regime não encontrado"));
            category.setRegime(regime);
        } else {
            category.setRegime(null);
        }

        return toCategoryDTO(categoryRepository.save(category));
    }

    public void deleteCategory(UUID id) {
        if (agentRepository.existsBySalaryCategory(id)) {
            throw new IllegalArgumentException(
                    "Não é possível excluir a categoria pois existem agentes vinculados a ela");
        }
        categoryRepository.deleteById(id);
    }

    private SalaryCategoryDTO toCategoryDTO(SalaryCategory category) {
        return SalaryCategoryDTO.builder()
                .id(category.getId())
                .code(category.getCode())
                .name(category.getName())
                .regimeId(category.getRegime() != null ? category.getRegime().getId() : null)
                .regimeName(category.getRegime() != null ? category.getRegime().getName() : null)
                .build();
    }

    // --- Grade methods ---
    public List<SalaryGradeDTO> getGradesByCategory(UUID categoryId) {
        return gradeRepository.findByCategoryId(categoryId).stream()
                .map(this::toGradeDTO)
                .collect(Collectors.toList());
    }

    public SalaryGradeDTO createGrade(SalaryGradeDTO dto) {
        SalaryCategory category = categoryRepository.findById(dto.getCategoryId())
                .orElseThrow(() -> new IllegalArgumentException("Categoria não encontrada"));

        SalaryGrade entity = new SalaryGrade();
        entity.setCategory(category);
        entity.setCode(dto.getCode());
        entity.setName(dto.getName());
        return toGradeDTO(gradeRepository.save(entity));
    }

    public SalaryGradeDTO updateGrade(UUID id, SalaryGradeDTO dto) {
        SalaryGrade grade = gradeRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Grau não encontrado"));

        grade.setCode(dto.getCode());
        grade.setName(dto.getName());
        return toGradeDTO(gradeRepository.save(grade));
    }

    public void deleteGrade(UUID id) {
        if (agentRepository.existsBySalaryGrade(id)) {
            throw new IllegalArgumentException("Não é possível excluir o grau pois existem agentes vinculados a ele");
        }
        gradeRepository.deleteById(id);
    }

    private SalaryGradeDTO toGradeDTO(SalaryGrade grade) {
        return SalaryGradeDTO.builder()
                .id(grade.getId())
                .categoryId(grade.getCategory().getId())
                .code(grade.getCode())
                .name(grade.getName())
                .build();
    }

    // --- Step methods ---
    public List<SalaryStepDTO> getStepsByGrade(UUID gradeId) {
        return stepRepository.findByGradeIdOrderByStepNumber(gradeId).stream()
                .map(this::toStepDTO)
                .collect(Collectors.toList());
    }

    public SalaryStepDTO createStep(SalaryStepDTO dto) {
        SalaryGrade grade = gradeRepository.findById(dto.getGradeId())
                .orElseThrow(() -> new IllegalArgumentException("Grade não encontrada"));

        SalaryStep entity = new SalaryStep();
        entity.setGrade(grade);
        entity.setStepNumber(dto.getStepNumber());
        return toStepDTO(stepRepository.save(entity));
    }

    public void deleteStep(UUID id) {
        if (agentRepository.existsBySalaryStep(id)) {
            throw new IllegalArgumentException(
                    "Não é possível excluir o escalão pois existem agentes vinculados a ele");
        }
        stepRepository.deleteById(id);
    }

    private SalaryStepDTO toStepDTO(SalaryStep step) {
        return SalaryStepDTO.builder()
                .id(step.getId())
                .gradeId(step.getGrade().getId())
                .stepNumber(step.getStepNumber())
                .build();
    }

    // --- Grid methods ---
    public List<SalaryGridDTO> getGridByStep(UUID stepId) {
        return gridRepository.findByStepId(stepId).stream()
                .map(this::toGridDTO)
                .collect(Collectors.toList());
    }

    public SalaryGridDTO createGridEntry(SalaryGridDTO dto) {
        SalaryStep step = stepRepository.findById(dto.getStepId())
                .orElseThrow(() -> new IllegalArgumentException("Step não encontrado"));

        SalaryGrid entity = new SalaryGrid();
        entity.setStep(step);
        entity.setValidFrom(dto.getValidFrom());
        entity.setValidTo(dto.getValidTo());
        entity.setBaseAmount(dto.getBaseAmount());

        return toGridDTO(gridRepository.save(entity));
    }

    private SalaryGridDTO toGridDTO(SalaryGrid grid) {
        return SalaryGridDTO.builder()
                .id(grid.getId())
                .stepId(grid.getStep().getId())
                .validFrom(grid.getValidFrom())
                .validTo(grid.getValidTo())
                .baseAmount(grid.getBaseAmount())
                .subsidyAmount(grid.getSubsidyAmount())
                .grossAmount(grid.getGrossAmount())
                .build();
    }
}
