<?php

namespace App\Traits;

use Illuminate\Support\Facades\Http;

trait LocationTrait
{
    /**
     * Get the profile's location and optionally generate a link to OpenStreetMap.
     * Attention: do not extensively use the $lookUpOsmLocation as too many request will be rate-limited!
     *
     * @param bool $lookUpOsmLocation Whether to fetch OSM coordinates, URL and polygon data
     * @return array
     */
    public function getLocationFirst($lookUpOsmLocation = false)
    {
        // Initialize variables
        $location = '';
        $country = '';
        $division = '';
        $city = '';
        $district = '';
        $locationData = [];

        if ($this->locations && $this->locations->isNotEmpty()) {
            // Use the first location if available
            $firstLocation = $this->locations->first();
        } else {
            // Fallback to the single location property
            $firstLocation = $this->location;
        }

        if ($firstLocation) {
            if (isset($firstLocation->city)) {
                $cityTranslation = $firstLocation->city->translations->first();
                $city = $cityTranslation ? $cityTranslation->name : '';
                $location = $city;
            }
            if (isset($firstLocation->district)) {
                $districtTranslation = $firstLocation->district->translations->first();
                $district = $districtTranslation ? $districtTranslation->name : '';
                $location = $city ? $city . ' ' . $district : $district;
            }
            if (isset($firstLocation->division)) {
                $divisionTranslation = $firstLocation->division->translations->first();
                $division = $divisionTranslation ? $divisionTranslation->name : '';
                $location = $city || $district ? $location . ', ' . $division : $division;
            }
            if (isset($firstLocation->country)) {
                if ($firstLocation->country->code === 'XX') {
                    $country = __('Location not specified');
                    $countryName = $country;
                } else {
                    $country = $firstLocation->country->code;
                    $countryName = $firstLocation->country->translations->first()->name;
                }
                $location = $city || $district || $division ? $location . ', ' . $country : $country;
            }
        }

        // Remove trailing comma and space
        $locationName = rtrim($location, ', ');

        // Remove any space before a comma
        $locationName = preg_replace('/\s+,/', ',', $locationName);

        $locationData['name'] = $locationName;

        // Construct name_short based on available properties
        $nameShortParts = [];


        if ($city) {
            $nameShortParts[] = $city;
        }
        if ($district && count($nameShortParts) < 2) {
            $nameShortParts[] = $district;
        }
        if ($division && count($nameShortParts) < 2) {
            $nameShortParts[] = $division;
        }
        if (!$city && $country && count($nameShortParts) < 2) {
            $nameShortParts[] = $countryName;
        }


        // Join the parts with a space
        $locationData['name_short'] = implode(' ', $nameShortParts);

        // Remove any space before a comma
        $locationData['name_short'] = preg_replace('/\s+,/', ',', $locationData['name_short']);

        if ($lookUpOsmLocation == true) {
            // Construct the URL for Nominatim search with polygon data
            $searchUrl = 'https://nominatim.openstreetmap.org/search?format=json&polygon_geojson=1&q=' . urlencode($locationName);

            // Define your User-Agent
            $userAgent = config('app.name') . ' (' . timebank_config('mail.system_admin.email') . ')';

            // Send the HTTP request to the Nominatim API
            $response = Http::withHeaders([
                'User-Agent' => $userAgent,
            ])->get($searchUrl);

            // Parse the JSON response
            $data = $response->json();

            // Extract the first result's coordinates (if available)
            if (!empty($data[0])) {
                $latitude = $data[0]['lat'];
                $longitude = $data[0]['lon'];

                // Add coordinate data
                $locationData['latitude'] = $latitude;
                $locationData['longitude'] = $longitude;

                // Add polygon data if available
                if (isset($data[0]['geojson'])) {
                    $locationData['polygon'] = $data[0]['geojson'];
                    $locationData['polygon_type'] = $data[0]['geojson']['type'] ?? null;

                    // Create OpenStreetMap URL using polygon data (relation/way)
                    if (isset($data[0]['osm_type']) && isset($data[0]['osm_id'])) {
                        $osmType = $data[0]['osm_type'];
                        $osmId = $data[0]['osm_id'];

                        // Use relation or way URL for default polygon display with sidebar
                        if ($osmType === 'relation') {
                            $locationData['url'] = "https://www.openstreetmap.org/relation/{$osmId}";
                        } elseif ($osmType === 'way') {
                            $locationData['url'] = "https://www.openstreetmap.org/way/{$osmId}";
                        } else {
                            // Fallback to coordinate-based URL with center marker
                            $locationData['url'] = "https://www.openstreetmap.org/?mlat={$latitude}&mlon={$longitude}#map=12/{$latitude}/{$longitude}&layers=V";
                        }
                    } else {
                        // Fallback to coordinate-based URL with center marker
                        $locationData['url'] = "https://www.openstreetmap.org/?mlat={$latitude}&mlon={$longitude}#map=12/{$latitude}/{$longitude}&layers=V";
                    }
                } else {
                    // No polygon data, use coordinate-based URL with center marker
                    $locationData['url'] = "https://www.openstreetmap.org/?mlat={$latitude}&mlon={$longitude}#map=12/{$latitude}/{$longitude}&layers=V";
                }

                // Add place information
                $locationData['place_id'] = $data[0]['place_id'] ?? null;
                $locationData['osm_type'] = $data[0]['osm_type'] ?? null;
                $locationData['osm_id'] = $data[0]['osm_id'] ?? null;
                $locationData['display_name'] = $data[0]['display_name'] ?? null;
            } else {
                $locationData['url'] = null; // No location found
            }
        }

        return $locationData;
    }


    private function getOsmUrl($locationName)
    {
        $searchUrl = 'https://nominatim.openstreetmap.org/search?format=json&q=' . urlencode($locationName);

            // Define your User-Agent
            $userAgent = config('app.name') . ' (' . timebank_config('mail.system_admin.email') . ')';

            // Send the HTTP request to the Nominatim API
            $response = Http::withHeaders([
                'User-Agent' => $userAgent,
            ])->get($searchUrl);

            // Parse the JSON response
            $data = $response->json();

            // Extract the first result's coordinates (if available)
            if (!empty($data[0])) {
                $latitude = $data[0]['lat'];
                $longitude = $data[0]['lon'];

                // Create the OpenStreetMap URL with the coordinates
                $locationData['url'] = "https://www.openstreetmap.org/?mlat={$latitude}&mlon={$longitude}#map=12/{$latitude}/{$longitude}";
            } else {
                $locationData['url'] = null; // No location found
            }


        return $locationData;
    }

    /**
     * Get polygon boundary for a specific location
     *
     * @param string $locationName The location to search for
     * @param array $options Additional search options (type, limit, etc.)
     * @return array|null Returns polygon data or null if not found
     */
    public function getLocationPolygon($locationName, $options = [])
    {
        // Default options
        $defaultOptions = [
            'type' => null, // e.g., 'city', 'administrative', 'boundary'
            'limit' => 1,
            'admin_level' => null, // e.g., 8 for cities, 4 for states
        ];

        $options = array_merge($defaultOptions, $options);

        // Construct the URL for Nominatim search
        $searchUrl = 'https://nominatim.openstreetmap.org/search?format=json&polygon_geojson=1&q=' . urlencode($locationName);

        // Add optional filters
        if ($options['limit']) {
            $searchUrl .= '&limit=' . $options['limit'];
        }

        // Define your User-Agent
        $userAgent = config('app.name') . ' (' . timebank_config('mail.system_admin.email') . ')';

        try {
            // Send the HTTP request to the Nominatim API
            $response = Http::timeout(10)->withHeaders([
                'User-Agent' => $userAgent,
            ])->get($searchUrl);

            // Parse the JSON response
            $data = $response->json();

            if (empty($data)) {
                return null;
            }

            // Filter results by type if specified
            if ($options['type']) {
                $data = array_filter($data, function($item) use ($options) {
                    return isset($item['type']) && $item['type'] === $options['type'];
                });
            }

            // Filter by admin level if specified
            if ($options['admin_level']) {
                $data = array_filter($data, function($item) use ($options) {
                    return isset($item['admin_level']) && $item['admin_level'] == $options['admin_level'];
                });
            }

            // Get the first (best) result
            $result = reset($data);

            if (!$result || !isset($result['geojson'])) {
                return null;
            }

            return [
                'polygon' => $result['geojson'],
                'polygon_type' => $result['geojson']['type'] ?? null,
                'coordinates_count' => $this->countPolygonCoordinates($result['geojson']),
                'latitude' => $result['lat'] ?? null,
                'longitude' => $result['lon'] ?? null,
                'display_name' => $result['display_name'] ?? null,
                'place_id' => $result['place_id'] ?? null,
                'osm_type' => $result['osm_type'] ?? null,
                'osm_id' => $result['osm_id'] ?? null,
                'boundingbox' => $result['boundingbox'] ?? null,
            ];

        } catch (\Exception $e) {
            // Log error if needed
            \Log::warning('Nominatim polygon request failed: ' . $e->getMessage());
            return null;
        }
    }

    /**
     * Count the number of coordinates in a GeoJSON polygon
     * Useful for determining polygon complexity
     *
     * @param array $geojson
     * @return int
     */
    private function countPolygonCoordinates($geojson)
    {
        if (!isset($geojson['coordinates'])) {
            return 0;
        }

        $count = 0;
        if ($geojson['type'] === 'Polygon') {
            foreach ($geojson['coordinates'] as $ring) {
                $count += count($ring);
            }
        } elseif ($geojson['type'] === 'MultiPolygon') {
            foreach ($geojson['coordinates'] as $polygon) {
                foreach ($polygon as $ring) {
                    $count += count($ring);
                }
            }
        }

        return $count;
    }

    /**
     * Get simplified polygon for display purposes
     * Nominatim returns detailed polygons that might be too complex for some uses
     *
     * @param string $locationName
     * @return array|null
     */
    public function getSimplifiedLocationPolygon($locationName)
    {
        $polygon = $this->getLocationPolygon($locationName);

        if (!$polygon || !$polygon['polygon']) {
            return null;
        }

        // If polygon has too many coordinates, you might want to simplify it
        // This is a basic example - you might want to use a proper simplification algorithm
        if ($polygon['coordinates_count'] > 1000) {
            // For very complex polygons, you might want to:
            // 1. Use a different endpoint
            // 2. Implement coordinate simplification
            // 3. Use bounding box instead

            $polygon['simplified'] = true;
            $polygon['original_coordinates_count'] = $polygon['coordinates_count'];
        }

        return $polygon;
    }
}
