<?php

namespace App\Http\Controllers\Api\Delivery;

use App\Http\Controllers\Controller;
use App\Models\DeliveryAssignment;
use App\Models\DeliveryPickupStop;
use App\Models\OrderStatusHistory;
use App\Notifications\Notify;
use App\Services\DeliveryAssignmentService;
use App\Services\ImageService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class AssignmentController extends Controller
{
    public function __construct(
        private DeliveryAssignmentService $dispatcher,
        private ImageService $images,
    ) {}

    /**
     * GET /api/delivery/assignments/pending
     * Active offers awaiting accept/decline.
     */
    public function pending(): JsonResponse
    {
        $partner = Auth::guard('delivery')->user();

        $offers = DeliveryAssignment::where('delivery_partner_id', $partner->id)
            ->where('status', 'offered')
            ->where('offer_expires_at', '>', now())
            ->with([
                'order:id,order_number,delivery_address,delivery_locality,grand_total,payment_method',
                'stops.vendor:id,store_name,phone,address,locality,lat,lng',
            ])
            ->get();

        return response()->json(['data' => $offers]);
    }

    /**
     * GET /api/delivery/assignments/current
     * Currently in-progress assignment for this partner.
     */
    public function current(): JsonResponse
    {
        $partner = Auth::guard('delivery')->user();

        $current = DeliveryAssignment::where('delivery_partner_id', $partner->id)
            ->whereIn('status', ['accepted', 'arrived_at_vendor', 'picked_up', 'on_the_way', 'arrived_at_customer'])
            ->with([
                'order:id,order_number,user_id,delivery_address,delivery_locality,delivery_lat,delivery_lng,grand_total,payment_method,delivery_otp',
                'order.user:id,name,phone',
                'order.items',
                'stops.vendor:id,store_name,phone,address,locality,lat,lng',
            ])
            ->first();

        return response()->json(['data' => $current]);
    }

    /**
     * GET /api/delivery/assignments/history
     */
    public function history(Request $request): JsonResponse
    {
        $partner = Auth::guard('delivery')->user();

        return response()->json([
            'data' => DeliveryAssignment::where('delivery_partner_id', $partner->id)
                ->whereIn('status', ['delivered', 'returned_to_vendor', 'cancelled', 'failed_delivery'])
                ->with('order:id,order_number,grand_total')
                ->latest('delivered_at')
                ->paginate($request->integer('per_page', 20)),
        ]);
    }

    public function accept(DeliveryAssignment $assignment): JsonResponse
    {
        $partner = Auth::guard('delivery')->user();
        $this->dispatcher->accept($assignment, $partner);

        return response()->json([
            'message'    => 'Offer accepted.',
            'assignment' => $assignment->fresh(['order.user', 'vendor']),
        ]);
    }

    public function decline(DeliveryAssignment $assignment): JsonResponse
    {
        $partner = Auth::guard('delivery')->user();
        $this->dispatcher->decline($assignment, $partner);

        return response()->json(['message' => 'Offer declined.']);
    }

    /**
     * POST /api/delivery/assignments/{assignment}/stops/{stop}/arrived
     * Courier reached one of the order's pickup stores.
     */
    public function arrivedAtStop(DeliveryAssignment $assignment, DeliveryPickupStop $stop): JsonResponse
    {
        $this->authorizeOwn($assignment);
        abort_if($stop->delivery_assignment_id !== $assignment->id, 404);
        abort_unless($assignment->status === 'accepted', 422, 'Bad assignment state.');

        $this->dispatcher->markStopArrived($stop);

        return response()->json(['message' => 'Arrived at store recorded.']);
    }

    /**
     * POST /api/delivery/assignments/{assignment}/stops/{stop}/picked-up
     * Courier collected this store's items. When every stop is collected the
     * order goes out for delivery and the customer gets the handoff OTP.
     */
    public function pickedUpStop(DeliveryAssignment $assignment, DeliveryPickupStop $stop): JsonResponse
    {
        $this->authorizeOwn($assignment);
        abort_if($stop->delivery_assignment_id !== $assignment->id, 404);
        abort_unless($assignment->status === 'accepted', 422, 'Bad assignment state.');

        $this->dispatcher->markStopPickedUp($stop);

        $onTheWay = $assignment->fresh()->status === 'on_the_way';

        return response()->json([
            'message'       => $onTheWay
                ? 'All items collected. Drive safe to the customer!'
                : 'Pickup recorded. Continue to the next store.',
            'all_picked_up' => $onTheWay,
        ]);
    }

    public function arrivedAtCustomer(DeliveryAssignment $assignment): JsonResponse
    {
        $this->authorizeOwn($assignment);
        abort_unless(in_array($assignment->status, ['on_the_way', 'picked_up']), 422, 'Bad state.');

        $assignment->update([
            'status'                 => 'arrived_at_customer',
            'arrived_at_customer_at' => now(),
        ]);

        return response()->json(['message' => 'Customer notified you have arrived.']);
    }

    /**
     * POST /api/delivery/assignments/{assignment}/deliver
     * Requires the customer's delivery OTP. Locks COD as collected.
     */
    public function deliver(Request $request, DeliveryAssignment $assignment): JsonResponse
    {
        $this->authorizeOwn($assignment);
        abort_unless(in_array($assignment->status, ['arrived_at_customer', 'on_the_way']), 422, 'Bad state.');

        $request->validate([
            'otp'         => 'required|string|size:4',
            'proof_photo' => 'nullable|image|max:5120',
        ]);

        if ($assignment->order->delivery_otp !== $request->otp) {
            return response()->json(['message' => 'Wrong OTP. Ask customer to recheck.'], 422);
        }

        $photoPath = null;
        if ($request->hasFile('proof_photo')) {
            $photoPath = $this->images->storePublic($request->file('proof_photo'), 'delivery_proofs', 800);
        }

        // Same completion logic the admin "mark delivered" override uses.
        $this->dispatcher->markDelivered($assignment, [
            'proof_photo'  => $photoPath,
            'otp_verified' => true,
            'actor_type'   => 'delivery_partner',
            'actor_id'     => $assignment->delivery_partner_id,
        ]);

        return response()->json([
            'message'         => 'Delivered. Earning of PKR ' . number_format($assignment->partner_earning) . ' credited.',
            'earning'         => (float) $assignment->partner_earning,
            'cod_collected'   => (float) $assignment->cod_amount,
        ]);
    }

    /**
     * POST /api/delivery/assignments/{assignment}/failed
     * Courier couldn't hand over the order (customer unreachable, wrong address…).
     * The 3rd attempt — or `return_to_vendor` — returns the goods and refunds a
     * prepaid customer.
     */
    public function failed(Request $request, DeliveryAssignment $assignment): JsonResponse
    {
        $this->authorizeOwn($assignment);
        abort_unless(
            in_array($assignment->status, ['picked_up', 'on_the_way', 'arrived_at_customer', 'failed_delivery']),
            422,
            'You can only report a failed delivery after pickup.'
        );

        $request->validate([
            'reason'           => 'required|string|max:300',
            'return_to_vendor' => 'sometimes|boolean',
        ]);

        $result = $this->dispatcher->markFailed($assignment, $request->reason, [
            'actor_type'       => 'delivery_partner',
            'actor_id'         => $assignment->delivery_partner_id,
            'return_to_vendor' => $request->boolean('return_to_vendor'),
        ]);

        return response()->json([
            'message'  => $result['returned']
                ? 'Marked as returned — please take the items back to the vendor.'
                : "Attempt {$result['attempts']} recorded. You can try again.",
            'returned' => $result['returned'],
            'attempts' => $result['attempts'],
        ]);
    }

    private function authorizeOwn(DeliveryAssignment $assignment): void
    {
        abort_unless(
            $assignment->delivery_partner_id === Auth::guard('delivery')->id(),
            403,
            'This assignment is not yours.'
        );
    }
}
