package com.ai.trackwalker.uavaccess.flighthub;

import com.ai.trackwalker.uavaccess.common.Jsons;
import com.ai.trackwalker.uavaccess.config.UavAccessProperties;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Service
public class DeviceTopologyService {
    private static final String DEFAULT_CONNECTION_ID = "flighthub2-default";

    private final FlightHubOpenApiClient client;
    private final UavAccessProperties properties;
    private final JdbcTemplate jdbc;

    public DeviceTopologyService(
            FlightHubOpenApiClient client,
            UavAccessProperties properties,
            JdbcTemplate jdbc
    ) {
        this.client = client;
        this.properties = properties;
        this.jdbc = jdbc;
    }

    @Transactional
    public List<Map<String, Object>> synchronize(String connectionId) {
        String resolvedConnection = normalizeConnectionId(connectionId);
        String project = properties.getFlighthub().getProjectUuid();
        Map<String, Object> data = client.get(
                "/openapi/v2.0/manage/api/v1/projects/" + project + "/topologies",
                Map.of()
        );
        List<Map<String, Object>> devices = flatten(data);
        Instant now = Instant.now();
        for (Map<String, Object> device : devices) {
            jdbc.update("""
                            insert into access_device_bindings(
                                connection_id,vendor_device_id,platform_device_id,parent_vendor_device_id,
                                project_uuid,device_model_key,device_class,capability_snapshot,state_snapshot,
                                online,last_seen_at,created_at,updated_at
                            ) values (?,?,?,?,?,?,?,?::jsonb,?::jsonb,?,?,?,?)
                            on conflict (connection_id,vendor_device_id) do update set
                                parent_vendor_device_id=excluded.parent_vendor_device_id,
                                project_uuid=excluded.project_uuid,
                                device_model_key=excluded.device_model_key,
                                device_class=excluded.device_class,
                                capability_snapshot=excluded.capability_snapshot,
                                state_snapshot=excluded.state_snapshot,
                                online=excluded.online,
                                last_seen_at=excluded.last_seen_at,
                                updated_at=excluded.updated_at
                            """,
                    resolvedConnection,
                    device.get("vendor_device_id"),
                    null,
                    device.get("parent_vendor_device_id"),
                    project,
                    device.get("device_model_key"),
                    device.get("device_class"),
                    Jsons.write(device.get("capabilities")),
                    Jsons.write(device.get("raw_state")),
                    device.get("status").equals("ONLINE"),
                    Timestamp.from(now),
                    Timestamp.from(now),
                    Timestamp.from(now)
            );
        }
        return devices;
    }

    @SuppressWarnings("unchecked")
    List<Map<String, Object>> flatten(Map<String, Object> data) {
        Object rawList = data.get("list");
        if (!(rawList instanceof List<?> topologies)) return List.of();
        List<Map<String, Object>> devices = new ArrayList<>();
        for (Object rawTopology : topologies) {
            if (!(rawTopology instanceof Map<?, ?> topology)) continue;
            Map<String, Object> host = asMap(topology.get("host"));
            String hostSn = text(host.get("device_sn"));
            if (!host.isEmpty()) devices.add(normalizeNode(host, parentSn(host)));
            Object parents = topology.get("parents");
            if (parents instanceof List<?> list) {
                for (Object item : list) {
                    Map<String, Object> node = asMap(item);
                    if (!node.isEmpty()) devices.add(normalizeNode(node, null));
                }
            }
            Object nodes = topology.get("nodes");
            if (nodes instanceof List<?> list) {
                for (Object item : list) {
                    Map<String, Object> node = asMap(item);
                    if (!node.isEmpty()) devices.add(normalizeNode(node, hostSn.isBlank() ? parentSn(node) : hostSn));
                }
            }
        }
        return devices;
    }

    private Map<String, Object> normalizeNode(Map<String, Object> node, String parentSn) {
        Map<String, Object> model = asMap(node.get("device_model"));
        Map<String, Object> state = asMap(node.get("device_state"));
        String deviceClass = text(model.get("class"));
        boolean online = Boolean.TRUE.equals(node.get("device_online_status"));
        List<String> capabilities = capabilities(deviceClass, state);
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("vendor_device_id", text(node.get("device_sn")));
        result.put("serial_number", text(node.get("device_sn")));
        result.put("parent_vendor_device_id", parentSn);
        result.put("device_type", normalizedType(deviceClass));
        result.put("device_class", deviceClass);
        result.put("device_model_key", text(model.get("key")));
        result.put("model", text(model.get("name")));
        result.put("status", online ? "ONLINE" : "OFFLINE");
        result.put("longitude", number(state.get("longitude")));
        result.put("latitude", number(state.get("latitude")));
        result.put("altitude_m", number(state.getOrDefault("height", state.get("elevation"))));
        result.put("capabilities", capabilities);
        result.put("health_detail", health(state));
        result.put("raw_state", state);
        return result;
    }

    private Map<String, Object> health(Map<String, Object> state) {
        Map<String, Object> health = new LinkedHashMap<>();
        putIfPresent(health, "battery_percent", state.get("battery_capacity_percent"));
        putIfPresent(health, "dock_drone_battery_percent", state.get("drone_charge_state_capacity_percent"));
        putIfPresent(health, "firmware_version", state.get("firmware_version"));
        putIfPresent(health, "wind_speed_mps", state.get("wind_speed"));
        putIfPresent(health, "rainfall", state.get("rainfall"));
        putIfPresent(health, "temperature_c", state.get("environment_temperature"));
        putIfPresent(health, "humidity_percent", state.get("environment_humidity"));
        putIfPresent(health, "network_type", state.get("network_state_type"));
        putIfPresent(health, "mode_code", state.get("mode_code"));
        return health;
    }

    private List<String> capabilities(String deviceClass, Map<String, Object> state) {
        List<String> capabilities = new ArrayList<>();
        capabilities.add("DEVICE_STATE");
        if ("drone".equalsIgnoreCase(deviceClass)) {
            capabilities.add("MISSION_EXECUTION");
            capabilities.add("TELEMETRY");
            capabilities.add("LIVE_STREAM");
            if (state.containsKey("rtk_number") || state.containsKey("rtk_fixed")) capabilities.add("RTK");
        }
        if ("airport".equalsIgnoreCase(deviceClass)) {
            capabilities.add("DOCK_TASK");
            capabilities.add("REMOTE_OPERATIONS");
        }
        return capabilities;
    }

    private String normalizedType(String deviceClass) {
        return switch (deviceClass.toLowerCase()) {
            case "drone" -> "AIRCRAFT";
            case "airport" -> "DOCK";
            case "payload" -> "PAYLOAD";
            case "remote_control" -> "REMOTE_CONTROLLER";
            default -> deviceClass.isBlank() ? "UNKNOWN" : deviceClass.toUpperCase();
        };
    }

    private String parentSn(Map<String, Object> node) {
        Object raw = node.get("parent_sns");
        if (raw instanceof List<?> list && !list.isEmpty()) return text(list.get(0));
        return null;
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> asMap(Object value) {
        if (value instanceof Map<?, ?> map) return (Map<String, Object>) map;
        return Map.of();
    }

    private String text(Object value) {
        return value == null ? "" : String.valueOf(value);
    }

    private Number number(Object value) {
        return value instanceof Number number ? number : null;
    }

    private void putIfPresent(Map<String, Object> target, String key, Object value) {
        if (value != null) target.put(key, value);
    }

    private String normalizeConnectionId(String value) {
        return value == null || value.isBlank() ? DEFAULT_CONNECTION_ID : value;
    }
}
