<?php

namespace App\Traits;

use App\Models\GIS\GisFeature;
use App\Models\GIS\GisFeatureCollection;
use App\Models\GIS\GisFeatureGeometry;
use App\Models\GIS\GisFeatureProperty;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

trait GisTrait
{
    public function listCollections(): JsonResponse
    {
        try {
            // Fetch all collections
            $collections = GisFeatureCollection::all();

            // Return main fields: uuid, name, description, hide
            $data = $collections->map(fn($c) => [
                'uuid' => $c->uuid,
                'name' => $c->name,
                'description' => $c->description,
                'hide' => (bool)$c->hide, // convert to boolean if needed
            ]);

            return response()->json([
                'status' => 'success',
                'data' => $data,
                'message' => 'Fetched collections list successfully',
            ], 200);

        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'Error fetching collections: ' . $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Convert feature geometries to structured coordinates with longitude/latitude keys.
     *
     * @param \Illuminate\Database\Eloquent\Collection $geometries
     * @return \Illuminate\Support\Collection
     */
    private function formatCoordinates($geometries)
    {
        return $geometries
            ->map(fn($geo) => collect(json_decode($geo->coordinates, true))
                ->map(fn($pair) => [
                    'longitude' => $pair[0],
                    'latitude' => $pair[1],
                ])
            )
            ->flatten(1);
    }

    /**
     * Check if the JSON is an array of GIS collections
     */
    private function isGisJsonArray(array $data): bool
    {
        return isset($data[0]['feature'], $data[0]['data']);
    }

    /**
     * Import GIS JSON data (full collection)
     */
    private function importGisJson(array $jsonData): JsonResponse
    {
        try {
            DB::transaction(function () use ($jsonData) {
                foreach ($jsonData as $collectionData) {
                    $collection = GisFeatureCollection::firstOrCreate(
                        ['name' => $collectionData['feature']],
                        ['description' => null]
                    );

                    foreach ($collectionData['data'] as $featureData) {
                        $this->updateOrCreateFeatures($collection, $featureData);
                    }
                }
            });

            return response()->json([
                'status' => 'success',
                'data' => null,
                'message' => 'Import completed successfully',
            ], 200);
        } catch (\Exception $e) {
            return $this->errorResponse($e, 'Error importing GIS JSON');
        }
    }

    /**
     * Update or create multiple features for a collection
     */
    public function updateOrCreateFeatures(GisFeatureCollection $collection, array $featuresData): JsonResponse
    {
        try {
            $this->syncCollectionFeatures($collection, $featuresData);

            return response()->json([
                'status' => 'success',
                'message' => 'Features synchronized successfully',
            ], 200);

        } catch (\Exception $e) {
            return $this->errorResponse($e, 'Error synchronizing features');
        }
    }

    /**
     * Method to handle complete feature synchronization for a collection
     */
    private function syncCollectionFeatures(GisFeatureCollection $collection, array $featuresData): void
    {
        // Get all current feature UUIDs for this collection
        $currentFeatureUuids = GisFeature::where('collection_id', $collection->uuid)
            ->pluck('uuid')
            ->toArray();

        $updatedFeatureUuids = [];

        // Update or create features from input data
        foreach ($featuresData as $featureData) {
            $feature = $this->updateOrCreateFeature($collection, $featureData);
            $updatedFeatureUuids[] = $feature->uuid;
        }

        // Find features that are in database but not in input data (should be deleted)
        $featuresToDelete = array_diff($currentFeatureUuids, $updatedFeatureUuids);

        // Delete orphaned features
        if (!empty($featuresToDelete)) {
            GisFeature::whereIn('uuid', $featuresToDelete)->delete();
            // Note: Due to cascade delete, related properties and geometries will also be deleted
        }
    }

    /**
     * Update or create a single feature with properties and geometry
     */
    private function updateOrCreateFeature(GisFeatureCollection $collection, array $featureData): GisFeature
    {
        // Get all current feature UUIDs for this collection
        $currentFeatureUuids = GisFeature::where('collection_id', $collection->uuid)
            ->pluck('uuid')
            ->toArray();

        $feature = GisFeature::updateOrCreate(
            ['uuid' => $featureData['id'] ?? (string)Str::uuid()],
            [
                'type' => 'Feature',
                'geometry_type' => ucfirst($featureData['type']),
                'active' => $featureData['Active'] ?? true,
                'hide' => $featureData['Hide'] ?? false,
                'zoom' => $featureData['zoom'] ?? null,
                'collection_id' => $collection->uuid,
            ]
        );

        $properties = [
            'text' => $featureData['text'] ?? null,
            'color' => $featureData['color'] ?? null,
            'caption_center' => $featureData['caption_center'] ?? null,
            'Shape_Area' => $featureData['Shape_Area'] ?? null,
            'Shape_Length' => $featureData['Shape_Length'] ?? null,
            'OBJECTID' => $featureData['OBJECTID'] ?? null,
            'ProvincName' => $featureData['ProvincName'] ?? null,
            'ProvinDesc' => $featureData['ProvinDesc'] ?? null,
        ];

        foreach ($properties as $key => $value) {
            if (!is_null($value)) {
                GisFeatureProperty::updateOrCreate(
                    ['feature_id' => $feature->uuid, 'name' => $key],
                    ['value' => (string)$value]
                );
            }
        }

        $geometryData = $featureData['latlngs'] ?? $featureData['coordinates'] ?? [];
        if (!empty($geometryData)) {
            GisFeatureGeometry::updateOrCreate(
                ['feature_id' => $feature->uuid],
                [
                    'coordinates' => json_encode($geometryData),
                    'raw_shape' => null,
                ]
            );
        }

        // Remove the current feature from the list (since it still exists)
        $currentFeatureUuids = array_diff($currentFeatureUuids, [$feature->uuid]);

        return $feature;
    }

    /**
     * Standard error response
     */
    private function errorResponse(\Exception $e, string $message, int $code = 500): JsonResponse
    {
        return response()->json([
            'status' => 'error',
            'data' => $e->getMessage(),
            'message' => $message,
        ], $code);
    }

    /**
     * Detect if request contains GIS JSON
     */
    private function isGisJson(Request $request): bool
    {
        try {
            $data = $request->json()->all();
            return is_array($data) && isset($data[0]['feature'], $data[0]['data']);
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * Update GIS data for a collection
     */
    private function updateGisData(GisFeatureCollection $collection, array $jsonData): JsonResponse
    {
        try {
            DB::transaction(function () use ($collection, $jsonData) {
                foreach ($jsonData as $collectionData) {
                    if ($collectionData['feature'] !== $collection->name) {
                        continue;
                    }
                    foreach ($collectionData['data'] as $featureData) {
                        $this->updateOrCreateFeatures($collection, $featureData);
                    }
                }
            });

            $collection->load('features.properties', 'features.geometries');
            return response()->json([
                'status' => 'success',
                'data' => $this->formatCollectionResponse($collection),
                'message' => 'Collection updated successfully',
            ], 200);
        } catch (\Exception $e) {
            return $this->errorResponse($e, 'Error updating GIS data');
        }
    }

    /**
     * Format collection with features for JSON response
     */
    private function formatCollectionResponse(GisFeatureCollection $collection): array
    {
        return [
            'feature' => $collection->name,
            'data' => $collection->features->map(function ($feature) {
                return [
                    'id' => $feature->uuid,
                    'type' => 'Feature',
                    'active' => $feature->active,
                    'hide' => $feature->hide,
                    'zoom' => $feature->zoom,
                    'properties' => $feature->properties->pluck('value', 'name'),
                    'coordinates' => $feature->geometries
                        ->map(fn($geo) => collect(json_decode($geo->coordinates, true))
                            ->map(fn($pair) => [
                                'longitude' => $pair[0],
                                'latitude' => $pair[1],
                            ])
                        )
                        ->flatten(1),
                ];
            }),
        ];
    }
}
