<?php

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Models\DriverGroup;
use App\Models\DriverGroupRoute;
use App\Models\DriverGroupRouteLocation;
use App\Models\DriverGroupRouteRun;
use App\Models\Driver;
use App\Models\Trip;
use App\Support\ApiResponder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Throwable;

class GroupRouteController extends Controller
{
    use ApiResponder;

    public function create(Request $request, DriverGroup $group): JsonResponse
    {
        $data = $this->routeData($request);
        // Route fees are configured once on the group. Keep the legacy per-route
        // fee columns populated for schemas where they are still NOT NULL.
        $data += [
            'stated_fee_mmk' => $group->started_route_fee_mmk,
            'waiting_fee_per_minute_mmk' => $group->waiting_fee_per_minute_mmk,
            'gate_fare_mmk' => 0,
            'airport_fare_mmk' => $group->airport_extra_mmk,
            'highway_bus_center_fare_mmk' => $group->highway_bus_center_extra_mmk,
            'other_fare_mmk' => 0,
        ];
        $route = $group->routes()->create($data);
        return $this->ok($route, 'Group route created.', 201);
    }

    public function update(Request $request, DriverGroup $group, DriverGroupRoute $route): JsonResponse
    {
        abort_unless($route->group_id === $group->id, 404);
        $route->update($this->routeData($request));
        return $this->ok($route->fresh(), 'Group route updated.');
    }

    private function routeData(Request $request): array
    {
        return $request->validate([
            'name' => ['required', 'string', 'max:120'], 'origin_name' => ['required', 'string', 'max:160'],
            'destination_name' => ['required', 'string', 'max:160'],
            'is_active' => ['sometimes', 'boolean'],
        ]) + ['is_active' => true];
    }

    public function start(Request $request, string $groupPublicId, DriverGroupRoute $route): JsonResponse
    {
        $data = $request->validate([
            'waiting_minutes' => ['required', 'integer', 'min:0', 'max:1440'],
            'gate_cost_mmk' => ['required', 'integer', 'min:0', 'max:1000000000'],
            'airport_selected' => ['required', 'boolean'], 'highway_bus_center_selected' => ['required', 'boolean'],
            'latitude' => ['required', 'numeric', 'between:-90,90'], 'longitude' => ['required', 'numeric', 'between:-180,180'],
        ]);
        $driver = $request->user()->driver;
        abort_unless($driver && $driver->status?->value === 'active', 403);
        $group = DriverGroup::where('public_id', $groupPublicId)->where('is_active', true)->firstOrFail();
        abort_unless($route->group_id === $group->id && $route->is_active, 404);
        abort_unless($driver->groups()->whereKey($group->id)->wherePivot('status', 'approved')->exists(), 403);
        try {
            $run = DB::transaction(function () use ($driver, $route, $group, $data) {
                Driver::query()->lockForUpdate()->findOrFail($driver->id);
                if (DriverGroupRouteRun::where('driver_id', $driver->id)->where('status', 'in_progress')->lockForUpdate()->exists()) {
                    throw new \RuntimeException('Complete your current group route first.');
                }
                if (Trip::where('driver_id', $driver->id)->whereIn('status', ['assigned', 'driver_en_route', 'driver_arrived', 'trip_started'])->exists()) {
                    throw new \RuntimeException('Finish your current customer trip before starting a group route.');
                }
                $airportExtra = $data['airport_selected'] ? $group->airport_extra_mmk : 0;
                $highwayExtra = $data['highway_bus_center_selected'] ? $group->highway_bus_center_extra_mmk : 0;
                $run = DriverGroupRouteRun::create([
                    'route_id' => $route->id, 'group_id' => $group->id, 'driver_id' => $driver->id, 'status' => 'in_progress', 'started_at' => now(),
                    'start_latitude' => $data['latitude'], 'start_longitude' => $data['longitude'], 'waiting_minutes' => $data['waiting_minutes'], 'special_fare_type' => 'none',
                    'airport_selected' => $data['airport_selected'], 'highway_bus_center_selected' => $data['highway_bus_center_selected'],
                    'stated_fee_mmk' => $group->started_route_fee_mmk, 'waiting_fee_per_minute_mmk' => $group->waiting_fee_per_minute_mmk,
                    'cost_per_kilometer_mmk' => $group->cost_per_kilometer_mmk, 'gate_cost_mmk' => $data['gate_cost_mmk'],
                    'airport_extra_mmk' => $airportExtra, 'highway_bus_center_extra_mmk' => $highwayExtra,
                    'special_fare_mmk' => $airportExtra + $highwayExtra,
                ]);
                DriverGroupRouteLocation::create(['route_run_id' => $run->id, 'latitude' => $data['latitude'], 'longitude' => $data['longitude'], 'captured_at' => now()]);
                return $run;
            });
            return $this->ok($run->load(['route', 'group']), 'Group route started.', 201);
        } catch (Throwable $e) {
            return $this->error($e->getMessage(), 'GROUP_ROUTE_START_FAILED', 409);
        }
    }

    public function active(Request $request): JsonResponse
    {
        $driver = $request->user()->driver;
        abort_unless($driver, 403);
        return $this->ok(DriverGroupRouteRun::with(['route', 'group', 'locations'])->where('driver_id', $driver->id)->where('status', 'in_progress')->latest()->first());
    }

    public function complete(Request $request, DriverGroupRouteRun $run): JsonResponse
    {
        $driver = $request->user()->driver;
        abort_unless($driver && $run->driver_id === $driver->id, 403);
        $data = $request->validate(['latitude' => ['required', 'numeric', 'between:-90,90'], 'longitude' => ['required', 'numeric', 'between:-180,180'], 'waiting_minutes' => ['required', 'integer', 'min:0', 'max:1440'], 'gate_cost_mmk' => ['required', 'integer', 'min:0', 'max:1000000000'], 'airport_selected' => ['required', 'boolean'], 'highway_bus_center_selected' => ['required', 'boolean']]);
        abort_unless($data['waiting_minutes'] === $run->waiting_minutes && $data['gate_cost_mmk'] === $run->gate_cost_mmk && $data['airport_selected'] === $run->airport_selected && $data['highway_bus_center_selected'] === $run->highway_bus_center_selected, 422, 'Route fare details changed. Refresh the active route before completing.');
        try {
            $completed = DB::transaction(function () use ($run, $data) {
                $locked = DriverGroupRouteRun::lockForUpdate()->findOrFail($run->id);
                if ($locked->status !== 'in_progress') throw new \RuntimeException('This route is already complete.');
                DriverGroupRouteLocation::create(['route_run_id' => $locked->id, 'latitude' => $data['latitude'], 'longitude' => $data['longitude'], 'captured_at' => now()]);
                $points = $locked->locations()->get(['latitude', 'longitude'])->push((object) ['latitude' => (float) $data['latitude'], 'longitude' => (float) $data['longitude']])->values();
                $distance = 0.0;
                for ($i = 1; $i < $points->count(); $i++) $distance += $this->distanceMeters((float) $points[$i - 1]->latitude, (float) $points[$i - 1]->longitude, (float) $points[$i]->latitude, (float) $points[$i]->longitude);
                $waitingTotal = $locked->waiting_minutes * $locked->waiting_fee_per_minute_mmk;
                $distanceTotal = (int) round(($distance / 1000) * $locked->cost_per_kilometer_mmk);
                $locked->update(['status' => 'completed', 'completed_at' => now(), 'end_latitude' => $data['latitude'], 'end_longitude' => $data['longitude'], 'distance_meters' => (int) round($distance), 'distance_total_mmk' => $distanceTotal, 'waiting_total_mmk' => $waitingTotal, 'total_fare_mmk' => $locked->stated_fee_mmk + $waitingTotal + $distanceTotal + $locked->gate_cost_mmk + $locked->airport_extra_mmk + $locked->highway_bus_center_extra_mmk]);
                return $locked->fresh(['route', 'group', 'driver.user', 'locations']);
            });
            return $this->ok($completed, 'Route completed and fare calculated.', 200);
        } catch (Throwable $e) {
            return $this->error($e->getMessage(), 'GROUP_ROUTE_COMPLETE_FAILED', 409);
        }
    }

    private function distanceMeters(float $lat1, float $lon1, float $lat2, float $lon2): float
    {
        $a = sin(deg2rad($lat2 - $lat1) / 2) ** 2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin(deg2rad($lon2 - $lon1) / 2) ** 2;
        return 6371000 * 2 * asin(min(1, sqrt($a)));
    }
}
