<?php

namespace App\Http\Controllers;

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;

class ProjectController extends Controller
{
    /** List all collections */
    public function index(): JsonResponse
    {
        try {
            $collections = GisFeatureCollection::all(['uuid', 'name', 'description']);
            return response()->json([
                'status' => 'success',
                'message' => 'Collections retrieved successfully',
                'data' => $collections
            ], 200);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'data' => $e->getMessage(),
                'message' => 'Error retrieving collections'
            ], 500);
        }
    }

    /** Create collection or import GIS JSON */
    public function store(Request $request): JsonResponse
    {
        DB::beginTransaction();
        try {
            if ($this->isGisJson($request)) {
                $data = $this->importGisJson($request->json()->all());
                DB::commit();
                return response()->json([
                    'status' => 'success',
                    'message' => 'GIS data imported successfully',
                    'data' => $data
                ], 201);
            }

            $request->validate([
                'name' => 'required|string|unique:gis_feature_collections,name',
                'description' => 'nullable|string',
            ]);

            $collection = GisFeatureCollection::create([
                'uuid' => Str::uuid(),
                'name' => $request->name,
                'description' => $request->description,
            ]);

            DB::commit();
            return response()->json([
                'status' => 'success',
                'message' => 'Collection created successfully',
                'data' => $collection
            ], 201);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'status' => 'error',
                'data' => $e->getMessage(),
                'message' => 'Error creating collection'
            ], 500);
        }
    }

    /** Show a single collection with features */
    public function show(GisFeatureCollection $collection): JsonResponse
    {
        try {
            $collection->load('features.properties', 'features.geometries');
            return response()->json([
                'status' => 'success',
                'message' => 'Collection retrieved successfully',
                'data' => $this->formatCollectionResponse($collection)
            ], 200);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'data' => $e->getMessage(),
                'message' => 'Error retrieving collection'
            ], 500);
        }
    }

    /** Show all collections with features */
    public function showAll(): JsonResponse
    {
        try {
            $collections = GisFeatureCollection::with('features.properties', 'features.geometries')->get();
            $data = $collections->map(fn($c) => $this->formatCollectionResponse($c));

            return response()->json([
                'status' => 'success',
                'message' => 'All collections retrieved successfully',
                'data' => $data
            ], 200);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'data' => $e->getMessage(),
                'message' => 'Error retrieving collections'
            ], 500);
        }
    }

    /** Update collection or import full GIS JSON for it */
    public function update(Request $request, GisFeatureCollection $collection): JsonResponse
    {
        DB::beginTransaction();
        try {
            if ($this->isGisJson($request)) {
                $jsonData = $request->json()->all();
                $jsonData = array_map(fn($c) => array_merge($c, ['feature' => $collection->name]), $jsonData);
                $data = $this->updateGisData($collection, $jsonData);
                DB::commit();
                return response()->json([
                    'status' => 'success',
                    'message' => 'GIS data updated successfully',
                    'data' => $data
                ], 200);
            }

            $this->updateBasicInfo($request, $collection);
            $collection->load('features.properties', 'features.geometries');

            DB::commit();
            return response()->json([
                'status' => 'success',
                'message' => 'Collection updated successfully',
                'data' => $this->formatCollectionResponse($collection)
            ], 200);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'status' => 'error',
                'data' => $e->getMessage(),
                'message' => 'Error updating collection'
            ], 500);
        }
    }

    /** Update only one feature in a collection */
    public function updateSingleFeature(Request $request, GisFeatureCollection $collection): JsonResponse
    {
        DB::beginTransaction();
        try {
            $request->validate([
                'id' => 'required|uuid|exists:gis_features,uuid',
            ]);

            $feature = GisFeature::where('uuid', $request->id)
                ->where('collection_id', $collection->uuid)
                ->firstOrFail();

            // Update properties if provided
            $properties = $request->only(['text', 'color', 'caption_center', 'Shape_Area', 'Shape_Length', 'OBJECTID', 'ProvincName', 'ProvinDesc']);
            foreach ($properties as $key => $value) {
                GisFeatureProperty::updateOrCreate(
                    ['feature_id' => $feature->uuid, 'name' => $key],
                    ['value' => (string)$value]
                );
            }

            // Update geometry if provided
            if ($request->has('latlngs')) {
                GisFeatureGeometry::updateOrCreate(
                    ['feature_id' => $feature->uuid],
                    ['coordinates' => json_encode($request->latlngs), 'raw_shape' => null]
                );
            }

            DB::commit();
            $feature->load('properties', 'geometries');

            return response()->json([
                'status' => 'success',
                'message' => 'Feature updated successfully',
                'data' => [
                    'id' => $feature->uuid,
                    'properties' => $feature->properties->pluck('value', 'name'),
                    'coordinates' => $feature->geometries->map(fn($g) => json_decode($g->coordinates, true))->flatten(1)
                ]
            ], 200);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'status' => 'error',
                'data' => $e->getMessage(),
                'message' => 'Error updating feature'
            ], 500);
        }
    }

    /** Delete collection with features */
    public function destroy(GisFeatureCollection $collection): JsonResponse
    {
        DB::beginTransaction();
        try {
            foreach ($collection->features as $feature) {
                $feature->properties()->delete();
                $feature->geometries()->delete();
            }
            $collection->features()->delete();
            $collection->delete();

            DB::commit();
            return response()->json([
                'status' => 'success',
                'message' => 'Collection deleted successfully'
            ], 200);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'status' => 'error',
                'data' => $e->getMessage(),
                'message' => 'Error deleting collection'
            ], 500);
        }
    }

    /** Update or import GIS data for collection (internal service) */
    private function updateGisData(GisFeatureCollection $collection, array $jsonData): array
    {
        $result = [];

        DB::transaction(function () use ($collection, $jsonData, &$result) {
            foreach ($jsonData as $collectionData) {
                if ($collectionData['feature'] !== $collection->name) continue;

                foreach ($collectionData['data'] as $featureData) {
                    $feature = GisFeature::updateOrCreate(
                        ['uuid' => $featureData['id'] ?? 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]
                            );
                        }
                    }

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

                $collection->load('features.properties', 'features.geometries');
                $result[] = $this->formatCollectionResponse($collection);
            }
        });

        return $result;
    }

    /** Import GIS data JSON (internal service) */
    private function importGisJson(array $jsonData): array
    {
        $result = [];

        DB::transaction(function () use ($jsonData, &$result) {
            foreach ($jsonData as $collectionData) {
                $collection = GisFeatureCollection::firstOrCreate(
                    ['name' => $collectionData['feature']],
                    ['description' => $collectionData['description'] ?? null]
                );

                foreach ($collectionData['data'] as $featureData) {
                    $feature = GisFeature::updateOrCreate(
                        ['uuid' => $featureData['id'] ?? 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]
                            );
                        }
                    }

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

                $collection->load('features.properties', 'features.geometries');
                $result[] = $this->formatCollectionResponse($collection);
            }
        });

        return $result;
    }

    /** Helper: check if request contains GIS JSON */
    private function isGisJson(Request $request): bool
    {
        $data = $request->json()->all();
        return is_array($data) && isset($data[0]['feature']) && isset($data[0]['data']);
    }

    /** Helper: update basic collection info */
    private function updateBasicInfo(Request $request, GisFeatureCollection $collection): void
    {
        $request->validate([
            'name' => "required|string|unique:gis_feature_collections,name,{$collection->uuid},uuid",
            'description' => 'nullable|string',
        ]);
        $collection->update($request->only(['name', 'description']));
    }

    /** Helper: format collection response */
    private function formatCollectionResponse(GisFeatureCollection $collection): array
    {
        return [
            'feature' => $collection->name,
            'data' => $collection->features->map(function ($feature) {
                return [
                    'id' => $feature->uuid,
                    'type' => strtolower($feature->geometry_type),
                    'active' => $feature->active,
                    'hide' => $feature->hide,
                    'zoom' => $feature->zoom,
                    'properties' => $feature->properties->pluck('value', 'name'),
                    'coordinates' => $feature->geometries->map(fn($g) => json_decode($g->coordinates, true))->flatten(1),
                ];
            }),
        ];
    }
}
