/*
 * Copyright 2024-2026 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.cloud.ai.studio.admin.builder.generator.service.dsl.adapters;

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

import com.alibaba.cloud.ai.studio.admin.builder.generator.model.AppMetadata;
import com.alibaba.cloud.ai.studio.admin.builder.generator.model.Variable;
import com.alibaba.cloud.ai.studio.admin.builder.generator.model.chatbot.ChatBot;
import com.alibaba.cloud.ai.studio.admin.builder.generator.model.workflow.Edge;
import com.alibaba.cloud.ai.studio.admin.builder.generator.model.workflow.Graph;
import com.alibaba.cloud.ai.studio.admin.builder.generator.model.workflow.Node;
import com.alibaba.cloud.ai.studio.admin.builder.generator.model.workflow.NodeType;
import com.alibaba.cloud.ai.studio.admin.builder.generator.model.workflow.Workflow;
import com.alibaba.cloud.ai.studio.admin.builder.generator.service.dsl.AbstractDSLAdapter;
import com.alibaba.cloud.ai.studio.admin.builder.generator.service.dsl.DSLDialectType;
import com.alibaba.cloud.ai.studio.admin.builder.generator.service.dsl.NodeDataConverter;
import com.alibaba.cloud.ai.studio.admin.builder.generator.service.dsl.Serializer;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

/**
 * CustomDSLAdapter converts spring ai alibaba DSL to App and vice versa.
 */
@Component
public class CustomDSLAdapter extends AbstractDSLAdapter {

	private final ObjectMapper objectMapper;

	public CustomDSLAdapter(@Qualifier("yaml") Serializer serializer, List<NodeDataConverter<?>> nodeDataConverters) {
		super(nodeDataConverters, serializer);
		this.objectMapper = new ObjectMapper();
		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

	}

	@Override
	public AppMetadata mapToMetadata(Map<String, Object> data) {
		Map<String, Object> metadataMap = (Map<String, Object>) data.get("metadata");
		AppMetadata metadata = objectMapper.convertValue(metadataMap, AppMetadata.class);
		metadata.setId(UUID.randomUUID().toString());
		return metadata;
	}

	@Override
	public Map<String, Object> metadataToMap(AppMetadata metadata) {
		Map<String, Object> data = new HashMap<>();
		data.put("metadata", objectMapper.convertValue(metadata, new TypeReference<>() {
		}));
		return data;
	}

	@Override
	public Workflow mapToWorkflow(Map<String, Object> data) {
		Workflow workflow = new Workflow();
		Map<String, Object> specMap = (Map<String, Object>) data.get("spec");
		if (specMap.containsKey("workflowVars")) {
			List<Map<String, Object>> variables = (List<Map<String, Object>>) specMap.get("workflowVars");
			List<Variable> workflowVars = variables.stream()
				.map(variable -> objectMapper.convertValue(variable, Variable.class))
				.toList();
			workflow.setWorkflowVars(workflowVars);
		}
		if (specMap.containsKey("envVars")) {
			List<Map<String, Object>> variables = (List<Map<String, Object>>) specMap.get("envVars");
			List<Variable> envVars = variables.stream()
				.map(variable -> objectMapper.convertValue(variable, Variable.class))
				.toList();
			workflow.setEnvVars(envVars);
		}
		if (specMap.containsKey("graph")) {
			Graph graph = constructGraph((Map<String, Object>) specMap.get("graph"));
			workflow.setGraph(graph);
		}
		return workflow;
	}

	private Graph constructGraph(Map<String, Object> data) {
		Graph graph = new Graph();
		List<Node> nodes = new ArrayList<>();
		List<Edge> edges = new ArrayList<>();
		// convert nodes
		if (data.containsKey("nodes")) {
			List<Map<String, Object>> nodeMaps = (List<Map<String, Object>>) data.get("nodes");
			nodes = nodeMaps.stream().map(this::constructNode).toList();
		}
		// convert edges
		if (data.containsKey("edges")) {
			edges = objectMapper.convertValue(data.get("edges"), new TypeReference<>() {
			});
		}
		graph.setNodes(nodes);
		graph.setEdges(edges);
		return graph;
	}

	private Node constructNode(Map<String, Object> nodeMap) {
		Map<String, Object> nodeDataMap = (Map<String, Object>) nodeMap.remove("data");
		Node node = objectMapper.convertValue(nodeMap, Node.class);
		NodeType nodeType = node.getType();
		NodeDataConverter<?> nodeDataConverter = getNodeDataConverter(nodeType);
		node.setData(nodeDataConverter.parseMapData(nodeDataMap, DSLDialectType.CUSTOM));
		return node;
	}

	@Override
	public Map<String, Object> workflowToMap(Workflow workflow) {
		Map<String, Object> data = new HashMap<>();
		data.put("spec", objectMapper.convertValue(workflow, new TypeReference<>() {
		}));
		return data;
	}

	// TODO
	@Override
	public ChatBot mapToChatBot(Map<String, Object> data) {
		return null;
	}

	// TODO
	@Override
	public Map<String, Object> chatbotToMap(ChatBot chatBot) {
		return null;
	}

	@Override
	public void validateDSLData(Map<String, Object> data) {
		Map<String, Object> metadataMap = (Map<String, Object>) data.get("metadata");
		if (metadataMap == null || !metadataMap.containsKey("mode")) {
			throw new IllegalArgumentException("invalid dsl");
		}
	}

	@Override
	public Serializer getSerializer() {
		return serializer;
	}

	@Override
	public Boolean supportDialect(DSLDialectType dialectType) {
		return DSLDialectType.CUSTOM.equals(dialectType);
	}

}
