package com.ikon.projectmanagement.service.etl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ikon.connector.dto.response.FieldsConfigDto;
import com.ikon.connector.spi.ConnectorDataSync;
import com.ikon.projectmanagement.entity.WorkingDaysDetailsEntity;
import com.ikon.projectmanagement.entity.WorkingDaysEntity;
import com.ikon.projectmanagement.repository.WorkingDaysRepository;

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

@Component
@RequiredArgsConstructor
@Slf4j
public class WorkingDaysConnector implements ConnectorDataSync {

    private final WorkingDaysRepository workingDaysRepository;
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public List<FieldsConfigDto> getFieldsConfig() {
        List<FieldsConfigDto> fieldsConfig = new ArrayList<>();
        fieldsConfig.add(FieldsConfigDto.builder().key("workingId").label("Working ID").type("string").build());
        fieldsConfig.add(
                FieldsConfigDto.builder().key("accountIdentifier").label("Account Identifier").type("string").build());
        fieldsConfig.add(FieldsConfigDto.builder().key("workingDaysDetails").label("Working Days Details")
                .type("object").build());
        return fieldsConfig;
    }

    @Override
    public String getModule() {
        return "WorkingDays";
    }

    @Override
    @Transactional
    public void syncBatch(List<Map<String, Object>> payload) {
        if (payload == null || payload.isEmpty())
            return;

        List<WorkingDaysEntity> workingDaysList = payload.stream()
                .map(data -> {
                    UUID workingId = parseUUID(data.get("workingId"));
                    UUID accountIdentifier = parseUUID(data.get("accountIdentifier"));

                    if (workingId == null) {
                        log.warn("Skipping record with null workingId");
                        return null;
                    }

                    // Fetch existing entity with details eagerly, or create new
                    WorkingDaysEntity workingDays = workingDaysRepository.findByIdWithDetails(workingId)
                            .orElseGet(() -> WorkingDaysEntity.builder()
                                    .workingId(workingId)
                                    .build());

                    workingDays.setAccountIdentifier(accountIdentifier);

                    log.info("RAW workingDaysDetails type: {}",
                            data.get("workingDaysDetails") == null ? "NULL"
                                    : data.get("workingDaysDetails").getClass().getName());
                    log.info("RAW workingDaysDetails value: {}", data.get("workingDaysDetails"));

                    Map<String, WorkingDaysDetailsEntity> detailsMap = buildDetailsMap(data.get("workingDaysDetails"));
                    log.info("Built detailsMap size: {}", detailsMap.size());

                    if (!detailsMap.isEmpty()) {
                        // Clear existing managed collection (orphanRemoval deletes old rows)
                        workingDays.getWorkingDaysDetails().clear();
                        // Repopulate via addWorkingDaysDetail to keep parent link intact
                        detailsMap.values().forEach(workingDays::addWorkingDaysDetail);
                        log.info("workingDaysDetails after repopulation: {}",
                                workingDays.getWorkingDaysDetails().size());
                    }

                    return workingDays;
                })
                .filter(Objects::nonNull)
                .toList();

        log.info("Saving {} WorkingDays entities", workingDaysList.size());
        workingDaysRepository.saveAll(workingDaysList);
    }

    private Map<String, WorkingDaysDetailsEntity> buildDetailsMap(Object rawDetails) {
        Map<String, WorkingDaysDetailsEntity> resultMap = new HashMap<>();

        JsonNode rootNode = toJsonNode(rawDetails);
        if (rootNode == null || rootNode.isNull() || !rootNode.isObject()) {
            log.warn("workingDaysDetails could not be parsed as an object, type: {}",
                    rawDetails == null ? "NULL" : rawDetails.getClass().getName());
            return resultMap;
        }

        // Outer loop: year keys e.g. "2025", "2026"
        rootNode.fields().forEachRemaining(yearEntry -> {
            String year = yearEntry.getKey();
            JsonNode monthMapNode = yearEntry.getValue();
            if (!monthMapNode.isObject())
                return;

            // Inner loop: month keys e.g. "January", "February"
            monthMapNode.fields().forEachRemaining(monthEntry -> {
                JsonNode fd = monthEntry.getValue();
                if (!fd.isObject())
                    return;

                String month = fd.hasNonNull("month")
                        ? fd.path("month").asText(null)
                        : monthEntry.getKey();

                if (month == null) {
                    log.warn("Skipping detail with null month for year: {}", year);
                    return;
                }

                Integer workingDays = fd.hasNonNull("workingDays")
                        ? fd.path("workingDays").asInt()
                        : null;

                WorkingDaysDetailsEntity detail = WorkingDaysDetailsEntity.builder()
                        .year(fd.hasNonNull("year") ? fd.path("year").asText(null) : year)
                        .month(month)
                        .workingDays(workingDays)
                        .build();

                resultMap.put(month, detail);
                log.debug("Added detail: year={}, month={}, workingDays={}", year, month, workingDays);
            });
        });

        return resultMap;
    }

    /**
     * Converts ObjectNode, Map, or JSON String → JsonNode
     */
    private JsonNode toJsonNode(Object rawDetails) {
        if (rawDetails == null)
            return null;

        if (rawDetails instanceof JsonNode) {
            return (JsonNode) rawDetails;
        }

        if (rawDetails instanceof String) {
            try {
                return objectMapper.readTree((String) rawDetails);
            } catch (Exception e) {
                log.error("Failed to parse workingDaysDetails JSON string: {}", e.getMessage());
                return null;
            }
        }

        if (rawDetails instanceof Map) {
            return objectMapper.valueToTree(rawDetails);
        }

        log.warn("Unrecognized workingDaysDetails type: {}", rawDetails.getClass().getName());
        return null;
    }

    // --- Utility Helpers ---

    private UUID parseUUID(Object value) {
        if (value == null)
            return null;
        try {
            return UUID.fromString(value.toString());
        } catch (Exception e) {
            log.warn("Failed to parse UUID from value: {}", value);
            return null;
        }
    }
}