package com.customer.work.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.github.wnameless.json.flattener.JsonFlattener;
import com.github.wnameless.json.unflattener.JsonUnflattener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

@Component
public class JsonUtils {

    protected static final Logger LOGGER = LoggerFactory.getLogger(JsonUtils.class);
    protected final ObjectMapper objectMapper;
    protected final JavaTimeModule javaTimeModule;

    public JsonUtils(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
        this.javaTimeModule = new JavaTimeModule();

        // Enable ObjectMapper for handling Instant as string
        this.objectMapper.registerModule(javaTimeModule);
        this.objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

    public JsonNode convertListToJsonNode(List<Object> list) {
        return objectMapper.valueToTree(list);
    }

    public JsonNode convertMapToJsonNode(Map<String, Object> map) {
        return objectMapper.valueToTree(map);
    }

    public Map<String, Object> convertJsonNodeToMap(JsonNode jsonNode) {
        return objectMapper.convertValue(jsonNode, new TypeReference<Map<String, Object>>() {});
    }

    public Map<String, Object> flatten(Object payload) {
        try {
            return JsonFlattener.flattenAsMap(objectMapper.writeValueAsString(payload));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public Map<String, Object> unflatten(Map<String, Object> flatVars) {
        try {
            return objectMapper.readValue(JsonUnflattener.unflatten(flatVars), Map.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public JsonNode convertJsonStringToJsonNode(String jsonString) {
        try {
            return objectMapper.readTree(jsonString);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public Map<String, Object> convertObjectNodeToMap(ObjectNode objectNode) {
        return objectMapper.convertValue(objectNode, new TypeReference<Map<String, Object>>() {});
    }

    public ObjectNode getEmptyObjectNode() {
        return objectMapper.createObjectNode();
    }

    public ObjectNode loadObjectNodeFromFile(String path) {
        try {
            return (ObjectNode) this.objectMapper.readTree(new File(path));
        } catch (IOException e) {
            LOGGER.debug(e.getMessage());
            return null;
        }
    }

    public ArrayNode convertObjectToArrayNode(Object object) {
        if (object == null) {
            LOGGER.debug("{}: Argument is null", this.getClass().getName());
            return null;
        }
        String obyTypeName = object.getClass().getName();
        if (obyTypeName.equals("java.util.Collections$EmptyList") || obyTypeName.equals("java.util.ArrayList")) {
            return objectMapper.convertValue(object, ArrayNode.class);
        } else if (obyTypeName.equals("com.fasterxml.jackson.databind.node.ArrayNode")) {
            return (ArrayNode) object;
        } else {
            LOGGER.debug("{}: {} is not implemented}", this.getClass().getName(), obyTypeName);
            return null;
        }
    }
}
