<?php

namespace Modules\AdminModule\Http\Controllers\Web\New\Admin;

use App\Http\Controllers\BaseController;
use App\Service\BaseServiceInterface;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\View\View;
use Modules\TripManagement\Service\Interface\SafetyAlertServiceInterface;
use Modules\UserManagement\Service\Interface\CustomerServiceInterface;
use Modules\UserManagement\Service\Interface\DriverServiceInterface;
use Modules\ZoneManagement\Service\Interface\ZoneServiceInterface;

class FleetMapViewController extends BaseController
{
    protected $zoneService;
    protected $safetyAlertService;
    protected $driverService;
    protected $customerService;

    public function __construct(ZoneServiceInterface $zoneService, SafetyAlertServiceInterface $safetyAlertService, DriverServiceInterface $driverService, CustomerServiceInterface $customerService)
    {
        parent::__construct($zoneService);
        $this->zoneService = $zoneService;
        $this->safetyAlertService = $safetyAlertService;
        $this->driverService = $driverService;
        $this->customerService = $customerService;
    }

    public function index(?Request $request, string $type = null): View|Collection|LengthAwarePaginator|null|callable|RedirectResponse
    {
        return parent::index($request, $type); // TODO: Change the autogenerated stub
    }

    public function fleetMap(?Request $request, $type = null)
    {
        $zones = $this->zoneService->getBy(relations: ['tripRequest.safetyAlerts']);
        $safetyAlertZones = $zones->filter(function ($zone) {
            return $zone->tripRequest->contains(function ($tripRequest) {
                return $tripRequest->safetyAlerts->where('status', PENDING)->isNotEmpty();
            });
        })->pluck('id')->toArray();
        $safetyAlertCount = $this->safetyAlertService->getBy(criteria: ['status' => PENDING])->count();

        if (array_key_exists('zone_id', $request->all()) && $request['zone_id']) {
            $zone = $this->zoneService->findOne(id: $request['zone_id']);
        } else {
            $zone = count($zones) ? $this->zoneService->findOne(id: $zones[0]->id) : null;
        }
        $safetyAlertLatestUserRoute = $safetyAlertCount > 0 ? $this->safetyAlertService->safetyAlertLatestUserRoute() : 'javascript:void(0)';
        $safetyAlert = $this->safetyAlertService->findOneBy(criteria: ['status' => PENDING], relations: ['sentBy',], orderBy: ['created_at' => 'desc']);
        $safetyAlertUserId = $safetyAlert?->sentBy?->id ?? null;

        // Calculate center lat/lng
        $latSum = 0;
        $lngSum = 0;
        $totalPoints = 0;
        $polygons = $zone ? json_encode([formatCoordinates(json_decode($zone?->coordinates[0]->toJson(), true)['coordinates'])]) : json_encode([[]]);
        if ($zone) {
            foreach (formatCoordinates(json_decode($zone?->coordinates[0]->toJson(), true)['coordinates']) as $point) {
                $latSum += $point->lat;
                $lngSum += $point->lng;
                $totalPoints++;
            }
        }
        $centerLat = $latSum / ($totalPoints == 0 ? 1 : $totalPoints);
        $centerLng = $lngSum / ($totalPoints == 0 ? 1 : $totalPoints);
        if ($zone) {
            $data = $this->fleetCommon($type, $zone, $request->all());
            $drivers = $data['drivers'] ?? [];
            $customers = $data['customers'] ?? [];
            $markers = $data['markers'];
            return view('adminmodule::fleet-map', compact('drivers', 'customers', 'zones', 'safetyAlertZones', 'safetyAlertCount', 'safetyAlertLatestUserRoute', 'safetyAlertUserId', 'type', 'markers', 'polygons', 'centerLat', 'centerLng'));
        }
        $drivers = [];
        $customers = [];
        $markers = json_encode([[]]);


        return view('adminmodule::fleet-map', compact('drivers', 'customers', 'zones', 'safetyAlertZones', 'safetyAlertCount', 'safetyAlertLatestUserRoute', 'safetyAlertUserId', 'type', 'markers', 'polygons', 'centerLat', 'centerLng'));
    }

    public function fleetMapDriverList(?Request $request, $type = null)
    {
        $zones = $this->zoneService->getBy(relations: ['tripRequest.safetyAlerts']);
        if (array_key_exists('zone_id', $request->all()) && $request['zone_id']) {
            $zone = $this->zoneService->findOne(id: $request['zone_id']);
        } else {
            $zone = count($zones) ? $this->zoneService->findOne(id: $zones[0]->id) : null;
        }
        if ($zone) {
            $data = $this->fleetCommon($type, $zone, $request->all());
            $drivers = $data['drivers'];
            return response()
                ->json(view('adminmodule::partials.fleet-map._fleet-map-driver-list', compact('drivers'))
                    ->render());
        }
        $drivers = [];
        return response()
            ->json(view('adminmodule::partials.fleet-map._fleet-map-driver-list', compact('drivers'))
                ->render());

    }

    public function fleetMapDriverDetails($id, Request $request)
    {
        $driverRelations = [
            'vehicle.model', 'lastLocations', 'userAccount', 'receivedReviews', 'driverTrips', 'driverDetails'
        ];
        $driver = $this->driverService->findOneBy(criteria: ['user_type' => DRIVER, 'id' => $id], relations: $driverRelations);
        $trip = $driver?->driverTrips()?->whereIn('current_status', [ACCEPTED, OUT_FOR_PICKUP, ONGOING])->where('type', RIDE_REQUEST)->first();
        $otherTrips = $driver?->driverTrips()->where('type', RIDE_REQUEST)->where('id', '!=', $trip?->id)->get();
        $otherTrips = $otherTrips->filter(function ($trip) {
            return $trip?->driverSafetyAlertPending;
        });
        return response()
            ->json(view('adminmodule::partials.fleet-map._fleet-map-driver-details', compact('driver', 'trip', 'otherTrips'))
                ->render());
    }

    public function fleetMapCustomerList(?Request $request, $type = null)
    {
        $zones = $this->zoneService->getBy(relations: ['tripRequest.safetyAlerts']);
        if (array_key_exists('zone_id', $request->all()) && $request['zone_id']) {
            $zone = $this->zoneService->findOne(id: $request['zone_id']);
        } else {
            $zone = count($zones) ? $this->zoneService->findOne(id: $zones[0]->id) : null;
        }
        if ($zone) {
            $data = $this->fleetCommon($type, $zone, $request->all());
            $customers = $data['customers'];
            return response()
                ->json(view('adminmodule::partials.fleet-map._fleet-map-customer-list', compact('customers'))
                    ->render());
        }
        $customers = [];
        return response()
            ->json(view('adminmodule::partials.fleet-map._fleet-map-customer-list', compact('customers'))
                ->render());

    }

    public function fleetMapCustomerDetails($id, Request $request)
    {
        $customerRelations = [
            'userAccount', 'customerTrips.driver.vehicle.category', 'customerTrips.driver.vehicle.brand', 'customerTrips.driver.vehicle.model'
        ];

        $customer = $this->customerService->findOneBy(criteria: ['user_type' => CUSTOMER, 'id' => $id], relations: $customerRelations);
        $trip = $customer?->customerTrips()?->whereIn('current_status', [ACCEPTED, OUT_FOR_PICKUP, ONGOING])->where('type', RIDE_REQUEST)->first();
        $otherTrips = $customer?->customerTrips()->where('type', RIDE_REQUEST)->where('id', '!=', $trip?->id)->get();
        $otherTrips = $otherTrips->filter(function ($trip) {
            return $trip?->customerSafetyAlertPending;
        });
        return response()
            ->json(view('adminmodule::partials.fleet-map._fleet-map-customer-details', compact('customer', 'trip', 'otherTrips'))
                ->render());
    }

    public function fleetMapViewUsingAjax(Request $request)
    {
        $type = $request->type;
        $zones = $this->zoneService->getAll();
        if (array_key_exists('zone_id', $request->all()) && $request['zone_id']) {
            $zone = $this->zoneService->findOne(id: $request['zone_id']);
        } else {
            $zone = count($zones) ? $this->zoneService->findOne(id: $zones[0]->id) : null;
        }
        // Calculate center lat/lng
        $latSum = 0;
        $lngSum = 0;
        $totalPoints = 0;
        $polygons = $zone ? json_encode([formatCoordinates(json_decode($zone?->coordinates[0]->toJson(), true)['coordinates'])]) : json_encode([[]]);
        if ($zone) {
            foreach (formatCoordinates(json_decode($zone?->coordinates[0]->toJson(), true)['coordinates']) as $point) {
                $latSum += $point->lat;
                $lngSum += $point->lng;
                $totalPoints++;
            }
        }
        $centerLat = $latSum / ($totalPoints == 0 ? 1 : $totalPoints);
        $centerLng = $lngSum / ($totalPoints == 0 ? 1 : $totalPoints);
        if ($zone) {
            $data = $this->fleetCommon($type, $zone, $request->all());
            $drivers = $data['drivers'] ?? [];
            $markers = $data['markers'];
            return response()
                ->json(['markers' => $markers, 'polygons' => $polygons, 'centerLat' => $centerLat, 'centerLng' => $centerLng]);

        }
        $markers = json_encode([[]]);
        return response()
            ->json(['markers' => $markers, 'polygons' => $polygons, 'centerLat' => $centerLat, 'centerLng' => $centerLng]);
    }

    public function fleetMapViewSingleDriver($id, Request $request)
    {
        $driverRelations = [
            'vehicle.model', 'lastLocations', 'userAccount', 'receivedReviews', 'driverTrips', 'driverDetails'
        ];
        $driver = $this->driverService->findOneBy(criteria: ['user_type' => DRIVER, 'id' => $id], relations: $driverRelations);

        $zones = $this->zoneService->getAll();
        if (array_key_exists('zone_id', $request->all()) && $request['zone_id']) {
            $zone = $this->zoneService->findOne(id: $request['zone_id']);
        } else {
            $zone = count($zones) ? $this->zoneService->findOne(id: $zones[0]->id) : null;
        }
        // Calculate center lat/lng
        $latSum = 0;
        $lngSum = 0;
        $totalPoints = 0;
        $polygons = $zone ? json_encode([formatCoordinates(json_decode($zone?->coordinates[0]->toJson(), true)['coordinates'])]) : json_encode([[]]);
        if ($zone) {
            foreach (formatCoordinates(json_decode($zone?->coordinates[0]->toJson(), true)['coordinates']) as $point) {
                $latSum += $point->lat;
                $lngSum += $point->lng;
                $totalPoints++;
            }
        }
        $centerLat = $latSum / ($totalPoints == 0 ? 1 : $totalPoints);
        $centerLng = $lngSum / ($totalPoints == 0 ? 1 : $totalPoints);
        if ($zone) {
            $markers = $this->generateMarker($driver, 'driver');
            $markers = json_encode([$markers]);
            return response()
                ->json(['markers' => $markers, 'polygons' => $polygons, 'centerLat' => $centerLat, 'centerLng' => $centerLng]);
        }
        $markers = json_encode([[]]);
        return response()
            ->json(['markers' => $markers, 'polygons' => $polygons, 'centerLat' => $centerLat, 'centerLng' => $centerLng]);
    }

    public function fleetMapViewSingleCustomer($id, Request $request)
    {
        $customerRelations = [
            'userAccount', 'customerTrips', 'lastLocations'
        ];

        $customer = $this->customerService->findOneBy(criteria: ['user_type' => CUSTOMER, 'id' => $id], relations: $customerRelations);

        $zones = $this->zoneService->getAll();
        if (array_key_exists('zone_id', $request->all()) && $request['zone_id']) {
            $zone = $this->zoneService->findOne(id: $request['zone_id']);
        } else {
            $zone = count($zones) ? $this->zoneService->findOne(id: $zones[0]->id) : null;
        }
        // Calculate center lat/lng
        $latSum = 0;
        $lngSum = 0;
        $totalPoints = 0;
        $polygons = $zone ? json_encode([formatCoordinates(json_decode($zone?->coordinates[0]->toJson(), true)['coordinates'])]) : json_encode([[]]);
        if ($zone) {
            foreach (formatCoordinates(json_decode($zone?->coordinates[0]->toJson(), true)['coordinates']) as $point) {
                $latSum += $point->lat;
                $lngSum += $point->lng;
                $totalPoints++;
            }
        }
        $centerLat = $latSum / ($totalPoints == 0 ? 1 : $totalPoints);
        $centerLng = $lngSum / ($totalPoints == 0 ? 1 : $totalPoints);
        if ($zone) {
            $markers = $this->generateMarker($customer);
            $markers = json_encode([$markers]);
            return response()
                ->json(['markers' => $markers, 'polygons' => $polygons, 'centerLat' => $centerLat, 'centerLng' => $centerLng]);
        }
        $markers = json_encode([[]]);
        return response()
            ->json(['markers' => $markers, 'polygons' => $polygons, 'centerLat' => $centerLat, 'centerLng' => $centerLng]);
    }

    public function fleetMapZoneMessage()
    {
        $safetyAlertCount = $this->safetyAlertService->getBy(criteria: ['status' => PENDING])->count();

        return response()->json(view('adminmodule::partials.fleet-map._safety-alert-get-zone-message', compact('safetyAlertCount'))->render());
    }

    public function fleetMapSafetyAlertIconInMap()
    {
        $safetyAlertCount = $this->safetyAlertService->getBy(criteria: ['status' => PENDING])->count();
        $safetyAlertLatestUserRoute = $safetyAlertCount > 0 ? $this->safetyAlertService->safetyAlertLatestUserRoute() : 'javascript:void(0)';
        $safetyAlert = $this->safetyAlertService->findOneBy(criteria: ['status' => PENDING], relations: ['sentBy',], orderBy: ['created_at' => 'desc']);
        $safetyAlertUserId = $safetyAlert?->sentBy?->id ?? null;

        return response()->json(view('adminmodule::partials.fleet-map._safety-alert-icon-in-map', compact('safetyAlertCount', 'safetyAlertLatestUserRoute', 'safetyAlertUserId'))->render());
    }

    private function fleetCommon($type, $zone, $request)
    {
        $searchCriteria = [];
        if (array_key_exists('search', $request)) {
            $searchCriteria = [
                'fields' => ['full_name', 'first_name', 'last_name', 'phone'],
                'value' => $request['search']
            ];
        }
        if ($type == ALL_DRIVER) {
            $driverCriteria = [
                'user_type' => DRIVER,
                'is_active' => 1,
            ];
            $driverRelations = [
                'vehicle.model', 'lastLocations', 'userAccount', 'receivedReviews', 'driverTrips', 'driverDetails'
            ];
            $driverWhereHasRelations = [
                'driverDetails' => ['is_online' => true],
                'lastLocations' => ['zone_id' => $zone->id],
            ];

            $drivers = $this->driverService->getBy(criteria: $driverCriteria, searchCriteria: $searchCriteria, whereHasRelations: $driverWhereHasRelations, relations: $driverRelations);
        } elseif ($type == DRIVER_ON_TRIP) {
            $driverCriteria = [
                'user_type' => DRIVER,
                'is_active' => 1,
            ];
            $driverRelations = [
                'vehicle.model', 'lastLocations', 'userAccount', 'receivedReviews', 'driverTrips', 'driverDetails'
            ];
            $driverWhereHasRelations = [
                'driverDetails' => ['is_online' => true],
                'lastLocations' => ['zone_id' => $zone->id],
                'driverTrips' => [
                    'type' => RIDE_REQUEST,
                    'current_status' => [ACCEPTED, OUT_FOR_PICKUP, ONGOING],
                ],
            ];
            $drivers = $this->driverService->getBy(criteria: $driverCriteria, searchCriteria: $searchCriteria, whereHasRelations: $driverWhereHasRelations, relations: $driverRelations);
        } elseif ($type == DRIVER_IDLE) {
            $driverCriteria = [
                'user_type' => DRIVER,
                'is_active' => 1,
            ];
            $driverRelations = [
                'vehicle.model', 'lastLocations', 'userAccount', 'receivedReviews', 'driverTrips', 'driverDetails'
            ];
            $driverWhereHasRelations = [
                'driverDetails' => ['is_online' => true],
                'lastLocations' => ['zone_id' => $zone->id],
            ];
            $drivers = $this->driverService->getBy(criteria: $driverCriteria, searchCriteria: $searchCriteria, whereHasRelations: $driverWhereHasRelations, relations: $driverRelations);
            $drivers = $drivers->filter(function ($driver) {
                return $driver->driverTrips
                        ->whereIn('current_status', [ACCEPTED, OUT_FOR_PICKUP, ONGOING])
                        ->where('type', RIDE_REQUEST)
                        ->count() < 1;
            })->values();
        } elseif ($type == ALL_CUSTOMER) {
            $customerCriteria = [
                'user_type' => CUSTOMER,
                'is_active' => 1,
            ];

            $customerRelations = [
                'lastLocations', 'userAccount', 'customerTrips'
            ];

            $customerWhereHasRelations = [
                'lastLocations' => ['zone_id' => $zone->id],
            ];

            $customers = $this->customerService->getBy(criteria: $customerCriteria, searchCriteria: $searchCriteria, whereHasRelations: $customerWhereHasRelations, relations: $customerRelations);

            $markers = $this->generateMarkers($customers);
            $markers = json_encode($markers);

            return [
                'markers' => $markers,
                'customers' => $customers];
        } else {
            abort(404);
        }
        $markers = $this->generateMarkers($drivers, 'driver');
        $markers = json_encode($markers);
        return [
            'drivers' => $drivers,
            'markers' => $markers,
        ];
    }

    private function generateMarker($entity, $type = 'customer')
    {
        $trip = ($type === 'customer')
            ? $entity?->customerTrips()?->whereIn('current_status', [ACCEPTED, OUT_FOR_PICKUP, ONGOING])->where('type', RIDE_REQUEST)->first()
            : $entity?->driverTrips()?->whereIn('current_status', [ACCEPTED, OUT_FOR_PICKUP, ONGOING])->where('type', RIDE_REQUEST)->first();
        $customerWhereHasRelations = [
            'sentBy' => [
                'user_type' => CUSTOMER,
                'id' => $entity?->id,
            ],
        ];
        $driverWhereHasRelations = [
            'sentBy' => [
                'user_type' => DRIVER,
                'id' => $entity?->id,
            ],
        ];
        $safetyAlert = ($type === 'customer')
            ? $this->safetyAlertService->getBy(criteria: ['status' => PENDING], whereHasRelations: $customerWhereHasRelations)->count()
            : $this->safetyAlertService->getBy(criteria: ['status' => PENDING], whereHasRelations: $driverWhereHasRelations)->count();

        $icon = match (true) {
            $trip && ($safetyAlert > 0) => dynamicAsset('public/assets/admin-module/img/maps/safety-alert-icon-on-active-trip.png'),
            !$trip && ($safetyAlert > 0) => dynamicAsset('public/assets/admin-module/img/maps/safety-alert-icon-on-idle-trip.png'),
            $trip && ($safetyAlert == 0) => dynamicAsset('public/assets/admin-module/img/maps/trip-active.png'),
            default => dynamicAsset('public/assets/admin-module/img/maps/trip-idle.png'),
        };

        return [
            'id' => $entity?->id,
            'position' => [
                'lat' => $entity?->lastLocations?->latitude ? (double)$entity?->lastLocations?->latitude : 0,
                'lng' => $entity?->lastLocations?->longitude ? (double)$entity?->lastLocations?->longitude : 0,
            ],
            'title' => $entity?->full_name ?? ($entity?->first_name ? $entity?->first_name . ' ' . $entity?->last_name : "N/A"),
            'subtitle' => $trip ? $trip->ref_id : null,
            "{$type}" => $entity?->id ? route("admin.{$type}.show", ['id' => $entity?->id]) : '#',
            'trip' => $trip ? route('admin.trip.show', ['type' => ALL, 'id' => $trip->id, 'page' => 'summary']) : '#',
            'icon' => $icon,
            'safetyAlertIcon' => $safetyAlert ? dynamicAsset('public/assets/admin-module/img/svg/shield-red.svg') : null,
        ];
    }

    private function generateMarkers($entities, $type = 'customer')
    {
        return $entities->map(fn($entity) => $this->generateMarker($entity, $type));
    }
}
