<?php

namespace Fleetbase\FleetOps\Support\Ai\Capabilities;

use Fleetbase\Ai\Contracts\AIActionCapabilityInterface;
use Fleetbase\Ai\Models\AiTask;
use Fleetbase\FleetOps\Models\Order;
use Fleetbase\FleetOps\Models\Place;
use Illuminate\Support\Facades\DB;

class OptimizeOrderRouteCapability extends AbstractFleetOpsAICapability implements AIActionCapabilityInterface
{
    public function key(): string
    {
        return 'fleet-ops.optimize_order_route';
    }

    public function label(): string
    {
        return 'Optimize Fleet-Ops order route';
    }

    public function description(): string
    {
        return 'Previews and applies confirmed waypoint resequencing for a Fleet-Ops order route.';
    }

    public function type(): string
    {
        return 'action';
    }

    public function mode(): string
    {
        return 'confirmation_required';
    }

    public function permissions(): array
    {
        return ['fleet-ops optimize order', 'fleet-ops update-route-for order'];
    }

    public function previewOnly(): bool
    {
        return false;
    }

    public function executable(): bool
    {
        return true;
    }

    public function inputSchema(): array
    {
        return [
            'order'     => 'Fleet-Ops order UUID or public ID.',
            'waypoints' => 'Ordered waypoint place UUIDs generated by the preview.',
        ];
    }

    public function shouldPreview(AiTask $task): bool
    {
        return $this->matchesPrompt($this->prompt($task));
    }

    public function resolve(AiTask $task): array
    {
        return $this->preview($task);
    }

    public function preview(AiTask $task, array $input = []): array
    {
        $authorized = $this->canAll($this->permissions());
        $orders     = $this->resolveOrders($task);

        if ($orders->count() > 1) {
            return [
                'action'         => $this->key(),
                'authorized'     => $authorized,
                'ready'          => false,
                'preview_only'   => true,
                'message'        => 'Fleetbase AI found multiple orders. Multi-order route sequencing should use the Fleet-Ops Orchestrator preview before commit.',
                'apply_label'    => 'Open Orchestrator',
                'cancel_label'   => 'Cancel',
                'missing_fields' => ['single order selection for direct route apply'],
                'fields'         => $orders->map(fn (Order $order) => ['label' => 'Order', 'value' => $order->public_id ?: $order->uuid])->values()->all(),
                'draft'          => ['orders' => $orders->pluck('public_id')->filter()->values()->all(), 'mode' => 'optimize_routes'],
                'risks'          => ['Multi-order optimization can affect manifests and should be committed through Orchestrator review.'],
            ];
        }

        $order = $orders->first();
        if (!$order) {
            return [
                'action'         => $this->key(),
                'authorized'     => $authorized,
                'ready'          => false,
                'message'        => 'Fleetbase AI needs a specific Fleet-Ops order ID before it can optimize a route.',
                'apply_label'    => 'Optimize route',
                'cancel_label'   => 'Cancel',
                'missing_fields' => ['order UUID or public ID'],
                'fields'         => [],
                'draft'          => [],
            ];
        }

        $order->loadMissing(['payload.pickup', 'payload.dropoff', 'payload.waypoints.place']);
        $waypoints = $order->payload?->waypoints?->sortBy('order')->values() ?? collect();
        $optimized = $this->optimizeWaypoints($order, $waypoints);
        $changed   = $waypoints->pluck('place_uuid')->values()->all() !== $optimized->pluck('place_uuid')->values()->all();
        $missing   = array_values(array_filter([
            $authorized ? null : 'permission to optimize and update Fleet-Ops order routes',
            $waypoints->count() >= 2 ? null : 'at least two route waypoints',
            $changed ? null : 'a route with a different optimized waypoint sequence',
        ]));

        return [
            'action'         => $this->key(),
            'authorized'     => $authorized,
            'ready'          => empty($missing),
            'message'        => empty($missing) ? 'Fleetbase AI prepared an optimized waypoint sequence. Review it before applying.' : 'Fleetbase AI cannot apply a route optimization until the route has optimizable waypoints.',
            'apply_label'    => 'Apply route',
            'cancel_label'   => 'Cancel',
            'missing_fields' => $missing,
            'fields'         => [
                ['label' => 'Order', 'value' => $order->public_id ?: $order->uuid],
                ['label' => 'Current sequence', 'value' => $this->stopLabels($waypoints)->implode(' -> ')],
                ['label' => 'Proposed sequence', 'value' => $this->stopLabels($optimized)->implode(' -> ')],
            ],
            'draft'          => [
                'order_uuid' => $order->uuid,
                'waypoints'  => $optimized->values()->map(fn ($waypoint, int $index) => [
                    'place_uuid' => $waypoint->place_uuid,
                    'order'      => $index,
                    'type'       => $waypoint->type ?: 'dropoff',
                ])->all(),
            ],
            'risks'          => ['Applying this changes the order waypoint sequence only after confirmation.'],
        ];
    }

    public function apply(AiTask $task, array $preview = [], array $input = []): array
    {
        if (!$this->canAll($this->permissions())) {
            throw new \RuntimeException('You do not have permission to optimize Fleet-Ops order routes.');
        }

        if (!data_get($preview, 'ready')) {
            throw new \RuntimeException('This route optimization preview is not ready to apply.');
        }

        $order = Order::where('company_uuid', session('company'))
            ->where('uuid', data_get($preview, 'draft.order_uuid'))
            ->with(['payload'])
            ->first();

        if (!$order || !$order->payload) {
            throw new \RuntimeException('Unable to find the Fleet-Ops order route to update.');
        }

        $waypoints = array_values((array) data_get($preview, 'draft.waypoints', []));
        if (count($waypoints) < 2) {
            throw new \RuntimeException('The route optimization preview does not include enough waypoints to apply.');
        }

        DB::transaction(function () use ($order, $waypoints) {
            $order->payload->updateWaypoints($waypoints);
            $order->is_route_optimized = true;
            $order->save();
        });

        return [
            'action'   => $this->key(),
            'status'   => 'completed',
            'message'  => 'Fleet-Ops order route was optimized.',
            'resource' => [
                'type'   => 'order',
                'id'     => $order->public_id,
                'uuid'   => $order->uuid,
                'route'  => 'console.fleet-ops.operations.orders.index.details',
                'models' => array_filter([$order->public_id ?: $order->uuid]),
            ],
        ];
    }

    protected function matchesPrompt(string $prompt): bool
    {
        return str_contains($prompt, 'order') && $this->containsAny($prompt, ['optimize route', 'optimise route', 'route optimization', 'route optimisation', 'optimize the route', 'optimise the route']);
    }

    protected function resolveOrders(AiTask $task)
    {
        $terms = $this->searchTerms((string) $task->prompt);

        return Order::where('company_uuid', session('company'))
            ->where(function ($query) use ($terms) {
                $this->whereLikeAny($query, ['uuid', 'public_id', 'internal_id'], $terms);
            })
            ->with(['payload.pickup', 'payload.dropoff', 'payload.waypoints.place'])
            ->limit(10)
            ->get();
    }

    protected function optimizeWaypoints(Order $order, $waypoints)
    {
        if ($waypoints->count() < 2) {
            return $waypoints;
        }

        $remaining = $waypoints->values();
        $optimized = collect();
        $cursor    = $order->payload?->pickup;

        while ($remaining->isNotEmpty()) {
            $nearest = $remaining
                ->sortBy(fn ($waypoint) => $this->distance($cursor, $waypoint->place))
                ->first();

            $optimized->push($nearest);
            $remaining = $remaining->reject(fn ($waypoint) => $waypoint->uuid === $nearest->uuid)->values();
            $cursor    = $nearest->place;
        }

        return $optimized;
    }

    protected function distance(?Place $from, ?Place $to): float
    {
        if (!$from || !$to || !is_numeric($from->lat) || !is_numeric($from->lng) || !is_numeric($to->lat) || !is_numeric($to->lng)) {
            return PHP_FLOAT_MAX;
        }

        return sqrt((float) (($from->lat - $to->lat) ** 2 + ($from->lng - $to->lng) ** 2));
    }

    protected function stopLabels($waypoints)
    {
        return $waypoints->map(fn ($waypoint) => $waypoint->place?->name ?: $waypoint->place?->public_id ?: $waypoint->place_uuid);
    }
}
