package de.assecutor.votianlt.service;

import de.assecutor.votianlt.model.LocationPosition;
import de.assecutor.votianlt.repository.LocationPositionRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;

/**
 * Service for handling location positions reported by mobile clients.
 * Automatically cleans up positions older than 60 minutes.
 */
@Service
@Slf4j
public class LocationService {

    private final LocationPositionRepository locationPositionRepository;

    public LocationService(LocationPositionRepository locationPositionRepository) {
        this.locationPositionRepository = locationPositionRepository;
    }

    /**
     * Save a position reported by a client.
     *
     * @param appUserId
     *            The app user ID
     * @param payload
     *            The position payload from the client
     */
    public void savePosition(String appUserId, Map<String, Object> payload) {
        try {
            Double latitude = extractDouble(payload.get("latitude"));
            Double longitude = extractDouble(payload.get("longitude"));

            if (latitude == null || longitude == null) {
                log.warn("[Location] Missing latitude or longitude from {}", appUserId);
                return;
            }

            Double accuracy = extractDouble(payload.get("accuracy"));
            Double altitude = extractDouble(payload.get("altitude"));
            Double speed = extractDouble(payload.get("speed"));
            Double heading = extractDouble(payload.get("heading"));
            Instant timestamp = extractInstant(payload.get("timestamp"));

            LocationPosition position = new LocationPosition(appUserId, latitude, longitude, accuracy, altitude, speed,
                    heading, timestamp);

            locationPositionRepository.save(position);
            log.debug("[Location] Saved position for {}: lat={}, lon={}", appUserId, latitude, longitude);

        } catch (Exception e) {
            log.error("[Location] Error saving position for {}: {}", appUserId, e.getMessage());
        }
    }

    /**
     * Get the latest position for a specific app user.
     *
     * @param appUserId
     *            The app user ID
     * @return The latest position or null if none found
     */
    public LocationPosition getLatestPosition(String appUserId) {
        List<LocationPosition> positions = locationPositionRepository
                .findTop1ByAppUserIdOrderByTimestampDesc(appUserId);
        return positions.isEmpty() ? null : positions.get(0);
    }

    /**
     * Get all positions for a specific app user from the last N minutes.
     *
     * @param appUserId
     *            The app user ID
     * @param minutes
     *            Number of minutes to look back
     * @return List of positions
     */
    public List<LocationPosition> getRecentPositions(String appUserId, int minutes) {
        Instant cutoff = Instant.now().minus(minutes, ChronoUnit.MINUTES);
        return locationPositionRepository.findByAppUserIdAndReceivedAtAfterOrderByTimestampDesc(appUserId, cutoff);
    }

    /**
     * Cleanup old positions. Runs every 5 minutes. Note: Positions also have a TTL
     * index that auto-deletes after 60 minutes, but this scheduled cleanup ensures
     * immediate removal and logging.
     */
    @Scheduled(fixedRate = 300000) // 5 minutes
    public void cleanupOldPositions() {
        try {
            Instant cutoff = Instant.now().minus(60, ChronoUnit.MINUTES);
            long countBefore = locationPositionRepository.count();
            locationPositionRepository.deleteByReceivedAtBefore(cutoff);
            long countAfter = locationPositionRepository.count();
            long deleted = countBefore - countAfter;

            if (deleted > 0) {
                log.info("[Location] Cleaned up {} positions older than 60 minutes", deleted);
            }
        } catch (Exception e) {
            log.error("[Location] Error during cleanup: {}", e.getMessage());
        }
    }

    /**
     * Extract Double from various number types.
     */
    private Double extractDouble(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Number) {
            return ((Number) value).doubleValue();
        }
        try {
            return Double.parseDouble(value.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    /**
     * Extract Instant from various formats.
     */
    private Instant extractInstant(Object value) {
        if (value == null) {
            return Instant.now();
        }
        if (value instanceof Instant) {
            return (Instant) value;
        }
        try {
            return Instant.parse(value.toString());
        } catch (Exception e) {
            return Instant.now();
        }
    }
}
