package com.coder.asset.status.change.serviceImpl;

import java.time.LocalDate;
import java.time.Year;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import com.coder.asset.status.change.client.AssetAllocationServiceClient;
import com.coder.asset.status.change.client.AssetServiceClient;
import com.coder.asset.status.change.client.CallLoggingServiceClient;
import com.coder.asset.status.change.dto.AssetAllocationDTO;
import com.coder.asset.status.change.dto.AssetDTO;
import com.coder.asset.status.change.dto.AssetStatusChangeDTO;
import com.coder.asset.status.change.dto.CallLoggingDTO;
import com.coder.asset.status.change.model.AssetStatusChange;
import com.coder.asset.status.change.model.SaveToAssetStatusChangeResponse;
import com.coder.asset.status.change.repository.AssetStatusChangeRepository;
import com.coder.asset.status.change.service.AssetStatusChangeService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@RequiredArgsConstructor
@Slf4j
public class AssetStatusChangeServiceImpl implements AssetStatusChangeService {

    @Autowired
    private final AssetStatusChangeRepository repository;
    
    @Autowired
	private AssetServiceClient assetServiceClient;
	    @Autowired
    private final CallLoggingServiceClient callLoggingServiceClient;
	    @Autowired
   private final AssetAllocationServiceClient assetAllocationServiceClient;
	    
    /* ================= FETCH ALL ================= */
	    @Override
	    public List<AssetStatusChangeDTO> fetchAllStatusChanges() {

	        List<AssetStatusChange> list = repository.findAll();

	        return list.stream()
	                .map(this::convertAssetStatusChangeIntoAssetStatusChangeDTO)
	                .collect(Collectors.toList());   // ✅ Collectors used
	    }

    /* ================= FETCH SINGLE ================= */
	    @Override
	    public AssetStatusChangeDTO getSingleStatusChange(String statusChangeId) {

	        return repository.findById(statusChangeId)
	                .map(this::convertAssetStatusChangeIntoAssetStatusChangeDTO)
	                .orElseThrow(() ->
	                        new NoSuchElementException("Status Change record does not exist!")
	                );
	    }

    /* ================= FETCH BY CREATED BY ================= */
	    @Override
	    public List<AssetStatusChangeDTO> getAllStatusChangesByCreatedBy(String createdBy) {

	        List<AssetStatusChange> entity = repository.findAllByCreatedBy(createdBy);

	        return entity.stream()
	                .map(this::convertAssetStatusChangeIntoAssetStatusChangeDTO)
	                .collect(Collectors.toList());   // ✅ Collectors used
	    }

    /* ================= SAVE ================= */
    
    @Override
    public SaveToAssetStatusChangeResponse saveAllStatusChanges(List<AssetStatusChange> statusList) {

        String prefix = "STATUS";
        int currentYear = Year.now().getValue();
        String idPrefix = prefix + "/" + currentYear + "/";

        List<AssetStatusChange> savedList = new ArrayList<>();
        List<String> duplicateMessages = new ArrayList<>();

        // 🔥 GET LAST ID
        List<String> existingIds =
                repository.findTopByPrefix(idPrefix, PageRequest.of(0, 1));

        int nextNumber = 1;

        if (!existingIds.isEmpty()) {
            String latestId = existingIds.get(0);
            String[] parts = latestId.split("/");

            if (parts.length == 3) {
                try {
                    nextNumber = Integer.parseInt(parts[2]) + 1;
                } catch (Exception e) {
                    nextNumber = 1;
                }
            }
        }

        /* ================= SAVE LOOP ================= */
        for (AssetStatusChange s : statusList) {

            Optional<AssetStatusChange> exists =
                    repository.checkDuplicateRecords(
                            s.getAssetId(),
                            s.getAssetSerialNumber(),
                            s.getChangeDate(),
                            s.getCreatedBy()
                    );

            if (exists.isPresent()) {
                duplicateMessages.add(
                        "Duplicate record for Asset ID: " + s.getAssetId()
                );
                continue;
            }

            String formattedNumber = String.format("%03d", nextNumber++);

            s.setStatusChangeId(idPrefix + formattedNumber);
            s.setChangeNumber(idPrefix + formattedNumber);

            s.setCreatedDate(LocalDate.now());
            s.setUpdatedDate(null);

            /* ================= 🔥 MAIN SYNC LOGIC ================= */
            try {

                // ✅ 1. UPDATE ASSET STATUS
                if (s.getAssetId() != null) {

                    String[] parts = s.getAssetId().split("/");

                    if (parts.length == 3) {

                        assetServiceClient.updateAssetStatus(
                                parts[0], parts[1], parts[2],
                                s.getNewStatus()
                        );
                    } else {
                        log.warn("Invalid Asset ID format: {}", s.getAssetId());
                    }
                }

                // ✅ 2. UPDATE CALL LOGGING STATUS
                if (s.getCallLoggingId() != null) {

                    String[] parts = s.getCallLoggingId().split("/");

                    if (parts.length == 3) {

                        callLoggingServiceClient.updateCallStatus(
                                parts[0], parts[1], parts[2],
                                s.getNewStatus()
                        );
                    } else {
                        log.warn("Invalid Call Logging ID format: {}", s.getCallLoggingId());
                    }
                }

                // ✅ 3. UPDATE ALLOCATION STATUS
                if (s.getAllocationId() != null) {

                    String[] parts = s.getAllocationId().split("/");

                    if (parts.length == 3) {

                        assetAllocationServiceClient.updateAllocationStatus(
                                parts[0], parts[1], parts[2],
                                s.getNewStatus()
                        );
                    } else {
                        log.warn("Invalid Allocation ID format: {}", s.getAllocationId());
                    }
                }

            } catch (Exception e) {
                log.error("Status Sync Failed for Asset: {}", s.getAssetId(), e);
            }
            /* ================= END ================= */

            savedList.add(s);
        }

        List<AssetStatusChange> saved = repository.saveAll(savedList);

        SaveToAssetStatusChangeResponse response =
                new SaveToAssetStatusChangeResponse();

        response.setSavedStatusChanges(saved);
        response.setDuplicateMessages(duplicateMessages);

        return response;
    }

    /* ================= UPDATE ================= */
    @Override
    public AssetStatusChange update(String statusChangeId, AssetStatusChange statusChange) {

        AssetStatusChange db = repository.findById(statusChangeId)
                .orElseThrow(() ->
                        new NoSuchElementException("Status Change record does not exist!"));

        db.setChangeNumber(statusChange.getChangeNumber());
        db.setChangeDate(statusChange.getChangeDate());

        db.setAssetId(statusChange.getAssetId());
        db.setAssetSerialNumber(statusChange.getAssetSerialNumber());

        db.setOldStatus(statusChange.getOldStatus());
        db.setNewStatus(statusChange.getNewStatus());

        db.setReasonForStatusChange(statusChange.getReasonForStatusChange());
        db.setDescription(statusChange.getDescription());

        db.setAllocationId(statusChange.getAllocationId());
        db.setCallLoggingId(statusChange.getCallLoggingId());
        db.setReplacementId(statusChange.getReplacementId());

        db.setChangedBy(statusChange.getChangedBy());
        db.setAssetCondition(statusChange.getAssetCondition());

        db.setRemarks(statusChange.getRemarks());

        db.setUpdatedBy(statusChange.getUpdatedBy());
        db.setUpdatedDate(LocalDate.now());

        return repository.saveAndFlush(db);
    }

    /* ================= DELETE ================= */
    @Override
    public boolean multipleDelete(List<String> statusChangeIds) {
        repository.deleteAllById(statusChangeIds);
        return true;
    }

    @Override
    public boolean deleteById(String statusChangeId) {

        AssetStatusChange existing = repository.findById(statusChangeId)
                .orElseThrow(() ->
                        new NoSuchElementException("Status Change record does not exist!"));

        repository.delete(existing);
        return true;
    }
    @Override
    
    public AssetStatusChangeDTO convertAssetStatusChangeIntoAssetStatusChangeDTO(
            AssetStatusChange asc) {

        AssetStatusChangeDTO dto = new AssetStatusChangeDTO();

        /* ================= BASIC ================= */
        dto.setStatusChangeId(asc.getStatusChangeId());
        dto.setChangeNumber(asc.getChangeNumber());
        dto.setChangeDate(asc.getChangeDate());

        dto.setAssetId(asc.getAssetId());
        dto.setAssetSerialNumber(asc.getAssetSerialNumber());

        dto.setOldStatus(asc.getOldStatus());
        dto.setNewStatus(asc.getNewStatus());

        dto.setReasonForStatusChange(asc.getReasonForStatusChange());
        dto.setDescription(asc.getDescription());

        dto.setAllocationId(asc.getAllocationId());
        dto.setCallLoggingId(asc.getCallLoggingId());
        dto.setReplacementId(asc.getReplacementId());

        dto.setChangedBy(asc.getChangedBy());
        dto.setAssetCondition(asc.getAssetCondition());

        dto.setRemarks(asc.getRemarks());

        dto.setCreatedBy(asc.getCreatedBy());
        dto.setCreatedDate(asc.getCreatedDate());

        dto.setUpdatedBy(asc.getUpdatedBy());
        dto.setUpdatedDate(asc.getUpdatedDate());

        /* ================= FETCH ASSET ================= */
        try {
            if (asc.getAssetId() != null) {

                String[] parts = asc.getAssetId().split("/");  // ✅ FIX

                if (parts.length == 3) {

                    AssetDTO asset = assetServiceClient.getSingleAsset(
                            parts[0], parts[1], parts[2]);

                    if (asset != null) {
                        dto.setAssetInfo(asset);
                    } else {
                        log.warn("Asset not found: {}", asc.getAssetId());
                    }

                } else {
                    log.warn("Invalid asset format: {}", asc.getAssetId());
                }
            }
        } catch (Exception e) {
            log.error("Error fetching asset for id: {}", asc.getAssetId(), e);
        }

        /* ================= FETCH ALLOCATION ================= */
        try {
            if (asc.getAllocationId() != null) {

                String[] parts = asc.getAllocationId().split("/");

                if (parts.length == 3) {

                    AssetAllocationDTO allocation =
                            assetAllocationServiceClient.getSingleAssetAllocationById(
                                    parts[0], parts[1], parts[2]);

                    if (allocation != null) {
                        dto.setAssetAllocationInfo(allocation);
                    } else {
                        log.warn("Allocation not found: {}", asc.getAllocationId());
                    }

                } else {
                    log.warn("Invalid allocation format: {}", asc.getAllocationId());
                }
            }
        } catch (Exception e) {
            log.error("Error fetching allocation: {}", asc.getAllocationId(), e);
        }

        /* ================= FETCH CALL LOGGING ================= */
        try {
            if (asc.getCallLoggingId() != null) {

                String[] parts = asc.getCallLoggingId().split("/");

                if (parts.length == 3) {

                    CallLoggingDTO call =
                            callLoggingServiceClient.getSingleCallLoggingInfo(
                                    parts[0], parts[1], parts[2]);

                    if (call != null) {
                        dto.setCallLoggingInfo(call);
                    } else {
                        log.warn("Call logging not found: {}", asc.getCallLoggingId());
                    }

                } else {
                    log.warn("Invalid call logging format: {}", asc.getCallLoggingId());
                }
            }
        } catch (Exception e) {
            log.error("Error fetching call logging: {}", asc.getCallLoggingId(), e);
        }

        return dto;
    }
}