package io.github.js0ny.ilp_coursework.data.common;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.github.js0ny.ilp_coursework.data.request.RegionCheckRequest;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Represents the data transfer object for a region definition
 *
 * <p>This record encapsulates the data for calculating if a coordinate is inside the region
 *
 * <p>A built-in method {@code isClosedTo} is defined to check this DTO is valid or not in the mean
 * of closing polygon
 *
 * @param name The human-readable name for the region
 * @param vertices list of coordinates that forms a polygon as a region.
 *     <p>In order to define a valid region, the last element of the list should be the same as the
 *     first, or known as closed
 * @see RegionCheckRequest
 * @see io.github.js0ny.ilp_coursework.service.GpsCalculationService#checkIsInRegion(LngLat, Region)
 */
public record Region(String name, List<LngLat> vertices) {
    /**
     * Magic number 4: For a polygon, 3 edges is required.
     *
     * <p>In this dto, edges + 1 vertices is required.
     */
    private static final int MINIMUM_VERTICES = 4;

    /**
     * Method to check if the region has a valid polygon by checking if the {@code vertices} forms a
     * closed polygon
     *
     * @return {@code true} if the {@code vertices} are able to form a polygon and form a closed
     *     polygon
     */
    public boolean isClosed() {
        if (vertices == null || vertices.size() < MINIMUM_VERTICES) {
            return false;
        }
        LngLat first = vertices.getFirst();
        LngLat last = vertices.getLast();
        return Objects.equals(last, first);
    }

    public Map<String, Object> toGeoJson() {
        try {
            ObjectMapper mapper = new ObjectMapper();

            List<List<Double>> ring =
                    vertices.stream().map(v -> List.of(v.lng(), v.lat())).toList();

            return Map.of("type", "Polygon", "coordinates", List.of(ring));

        } catch (Exception e) {
            throw new RuntimeException("Failed to generate GeoJSON", e);
        }
    }

    public String toGeoJsonString() {
        try {
            ObjectMapper mapper = new ObjectMapper();

            var geoJson = toGeoJson();

            return mapper.writeValueAsString(geoJson);
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate GeoJSON", e);
        }
    }
}
