package br.gov.sigefp.rh.service;

import br.gov.sigefp.rh.domain.*;
import br.gov.sigefp.rh.repository.AgentContractRepository;
import br.gov.sigefp.rh.repository.AgentRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;

/**
 * Serviço para gestão de contratos de agentes com sincronização automática.
 */
@Service
@RequiredArgsConstructor
@Transactional
public class AgentContractService {

    private final AgentContractRepository contractRepository;
    private final AgentRepository agentRepository;
    private final CareerEventService careerEventService;

    /**
     * Salva um novo contrato, desativando os anteriores e atualizando o agente.
     */
    /**
     * Atualiza um contrato existente e sincroniza dados se for o contrato ativo.
     */
    public AgentContract updateContract(AgentContract contract) {
        AgentContract existing = contractRepository.findById(contract.getId())
                .orElseThrow(() -> new IllegalArgumentException("Contrato não encontrado: " + contract.getId()));

        Agent agent = agentRepository.findById(contract.getAgent().getId())
                .orElseThrow(
                        () -> new IllegalArgumentException("Agente não encontrado: " + contract.getAgent().getId()));

        // Validar sobreposição
        validateContractOverlap(agent, existing);

        // Atualizar campos permitidos
        existing.setContractType(contract.getContractType());
        existing.setStartDate(contract.getStartDate());
        existing.setEndDate(contract.getEndDate());
        existing.setWeeklyHours(contract.getWeeklyHours());
        existing.setOrgUnit(contract.getOrgUnit());
        existing.setPosition(contract.getPosition());
        existing.setSalaryCategory(contract.getSalaryCategory());
        existing.setSalaryGrade(contract.getSalaryGrade());
        existing.setSalaryStep(contract.getSalaryStep());
        existing.setLegalActReference(contract.getLegalActReference());

        AgentContract saved = contractRepository.save(existing);

        // Se o contrato for o ativo, sincronizar os dados no Agente
        if (Boolean.TRUE.equals(saved.getIsActive())) {
            syncCareerData(agent, saved);

            String newAppointmentType = mapContractToAppointmentType(saved.getContractType());
            if (newAppointmentType != null) {
                agent.setAppointmentType(newAppointmentType);
            }
            agentRepository.save(agent);

            // Registrar Evento de Retificação
            careerEventService.recordEvent(agent, CareerEventType.RETIFICACAO,
                    "Retificação de Ato Administrativo: " + saved.getLegalActReference(),
                    saved.getLegalActReference(), saved.getStartDate(), LocalDate.now(),
                    saved.getSalaryCategory(), saved.getSalaryGrade(), saved.getSalaryStep(),
                    saved.getOrgUnit(), saved.getPosition(),
                    null, null, null, null, null);
        }

        return saved;
    }

    public AgentContract saveContract(AgentContract contract) {
        Agent agent = agentRepository.findById(contract.getAgent().getId())
                .orElseThrow(
                        () -> new IllegalArgumentException("Agente não encontrado: " + contract.getAgent().getId()));

        // Validar sobreposição antes de desativar
        validateContractOverlap(agent, contract);

        // 1. Desativar contratos ativos anteriores
        List<AgentContract> activeContracts = contractRepository.findByAgentId(agent.getId());
        for (AgentContract oldContract : activeContracts) {
            if (Boolean.TRUE.equals(oldContract.getIsActive())) {
                oldContract.setIsActive(false);
                contractRepository.save(oldContract);
            }
        }

        // 2. Definir o novo contrato como ativo
        contract.setIsActive(true);
        contract.setAgent(agent);
        AgentContract savedContract = contractRepository.save(contract);

        // 3. Sincronizar todos os dados de carreira do Contrato para o Agente
        syncCareerData(agent, savedContract);

        // 4. Ativação automática se a data de início for válida
        if (!savedContract.getStartDate().isAfter(LocalDate.now())) {
            agent.setStatus("ACTIVE");
        }

        // 5. Sincronizar appointmentType do Agente
        String newAppointmentType = mapContractToAppointmentType(contract.getContractType());
        if (newAppointmentType != null) {
            agent.setAppointmentType(newAppointmentType);
        }

        agentRepository.save(agent);

        // 6. Registar Evento de Carreira Automático via Serviço Centralizado
        recordAutomaticCareerEvent(agent, savedContract);

        return savedContract;
    }

    private void syncCareerData(Agent agent, AgentContract contract) {
        if (contract.getOrgUnit() != null)
            agent.setOrgUnit(contract.getOrgUnit());
        if (contract.getPosition() != null)
            agent.setPosition(contract.getPosition());
        if (contract.getSalaryCategory() != null)
            agent.setSalaryCategory(contract.getSalaryCategory());
        if (contract.getSalaryGrade() != null)
            agent.setSalaryGrade(contract.getSalaryGrade());
        if (contract.getSalaryStep() != null)
            agent.setSalaryStep(contract.getSalaryStep());
    }

    private String mapContractToAppointmentType(String contractType) {
        if (contractType == null)
            return null;
        return switch (contractType.toUpperCase()) {
            case "DEFINITIVA", "PERMANENT" -> "DEFINITIVA";
            case "PROVISORIA", "PROBATION" -> "PROVISORIA";
            case "FIXED_TERM", "CONTRATO", "TERMO" -> "CONTRATO_TERMO";
            case "PROVIMENTO" -> "CONTRATO_PROVIMENTO";
            default -> null;
        };
    }

    private void recordAutomaticCareerEvent(Agent agent, AgentContract contract) {
        CareerEventType eventType = switch (contract.getContractType().toUpperCase()) {
            case "DEFINITIVA" -> CareerEventType.NOMEACAO_DEFINITIVA;
            case "PROVISORIA" -> CareerEventType.NOMEACAO_PROVISORIA;
            default -> CareerEventType.ADMISSAO;
        };

        careerEventService.recordEvent(agent, eventType,
                "Ato Administrativo: " + contract.getLegalActReference() + " (" + contract.getContractType() + ")",
                contract.getLegalActReference(), contract.getStartDate(), LocalDate.now(),
                contract.getSalaryCategory(), contract.getSalaryGrade(), contract.getSalaryStep(),
                contract.getOrgUnit(), contract.getPosition(),
                null, null, null, null, null);
    }

    private void validateContractOverlap(Agent agent, AgentContract newContract) {
        List<AgentContract> existingContracts = contractRepository.findByAgentId(agent.getId());
        for (AgentContract existing : existingContracts) {
            // Skip if it's the same contract (update case)
            if (newContract.getId() != null && newContract.getId().equals(existing.getId())) {
                continue;
            }

            // Logic: Overlap if (StartA <= EndB) and (EndA >= StartB)
            // Handle null endDate as "Infinity"
            LocalDate startA = existing.getStartDate();
            LocalDate endA = existing.getEndDate();
            LocalDate startB = newContract.getStartDate();
            LocalDate endB = newContract.getEndDate();

            boolean startA_le_endB = (endB == null) || !startA.isAfter(endB);
            boolean endA_ge_startB = (endA == null) || !endA.isBefore(startB);

            if (startA_le_endB && endA_ge_startB) {
                throw new IllegalArgumentException(
                        String.format("Sobreposição detectada com contrato existente (ID: %s, Início: %s, Fim: %s)",
                                existing.getId(), startA, endA));
            }
        }
    }
}
