package io.github.js0ny.ilp_coursework.controller;

import io.github.js0ny.ilp_coursework.data.common.Angle;
import io.github.js0ny.ilp_coursework.data.common.LngLat;
import io.github.js0ny.ilp_coursework.data.common.Region;
import io.github.js0ny.ilp_coursework.data.request.DistanceRequest;
import io.github.js0ny.ilp_coursework.data.request.MovementRequest;
import io.github.js0ny.ilp_coursework.data.request.RegionCheckRequest;
import io.github.js0ny.ilp_coursework.service.GpsCalculationService;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Main REST Controller for the ILP Coursework 1 application.
 *
 * <p>This class handles incoming HTTP requests for the API under {@code /api/v1} path (defined in
 * CW1) This is responsible for mapping requests to the appropriate service method and returning the
 * results as responses. The business logic is delegated to {@link GpsCalculationService}
 */
@RestController
@RequestMapping("/api/v1")
public class ApiController {

    private final GpsCalculationService gpsService;

    /**
     * Constructor of the {@code ApiController} with the business logic dependency {@code
     * GpsCalculationService}
     *
     * @param gpsService The service component that contains all business logic, injected by
     *     Spring's DI.
     */
    public ApiController(GpsCalculationService gpsService) {
        this.gpsService = gpsService;
    }

    /**
     * Handles GET requests to retrieve the student's Unique ID
     *
     * @return A string representing the student ID starting with s
     */
    @GetMapping("/uid")
    public String getUid() {
        return "s2522255";
    }

    /**
     * Handles POST requests to get the distance between two positions
     *
     * @param request A {@link DistanceRequest} containing the two coordinates
     * @return A {@code double} representing the calculated distance
     */
    @PostMapping("/distanceTo")
    public double getDistance(@RequestBody DistanceRequest request) {
        LngLat position1 = request.position1();
        LngLat position2 = request.position2();
        return gpsService.calculateDistance(position1, position2);
    }

    /**
     * Handles POST requests to check if the two coordinates are close to each other
     *
     * @param request A {@link DistanceRequest} containing the two coordinates
     * @return {@code true} if the distance is less than the predefined threshold, {@code false}
     *     otherwise
     */
    @PostMapping("/isCloseTo")
    public boolean getIsCloseTo(@RequestBody DistanceRequest request) {
        LngLat position1 = request.position1();
        LngLat position2 = request.position2();
        return gpsService.isCloseTo(position1, position2);
    }

    /**
     * Handles POST requests to get the next position after an angle of movement
     *
     * @param request A {@link MovementRequest} containing the start coordinate and angle of the
     *     movement.
     * @return A {@link LngLat} representing the destination
     */
    @PostMapping("/nextPosition")
    public LngLat getNextPosition(@RequestBody MovementRequest request) {
        LngLat start = request.start();
        Angle angle = new Angle(request.angle());
        return gpsService.nextPosition(start, angle);
    }

    /**
     * Handles POST requests to check if a point is inside a given region
     *
     * @param request A {@link RegionCheckRequest} containing the coordinate and the region
     * @return {@code true} if the coordinate is inside the region, {@code false} otherwise
     */
    @PostMapping("/isInRegion")
    public boolean getIsInRegion(@RequestBody RegionCheckRequest request) {
        LngLat position = request.position();
        Region region = request.region();
        return gpsService.checkIsInRegion(position, region);
    }
}
