<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Booking;
use App\Models\BookingContact;
use App\Models\OperatorFarmedJob;
use App\Traits\GeneratesBookingNumber;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;

/**
 * Handles: Farm-Out Jobs (outgoing) + Received Jobs (incoming)
 *
 * Every farm-out job is mirrored into the bookings table so dispatchers
 * on both sides can see and manage the ride:
 *   • store()  → creates a Booking for the SENDER tenant (farm_option = 'Farm-out')
 *   • accept() → creates a Booking for the RECEIVER tenant (farm_option = 'Farm-in')
 *   • cancel() → cancels both linked bookings
 *   • complete()→ marks receiver booking as completed
 */
class OperatorFarmedJobController extends Controller
{
    use GeneratesBookingNumber;

    private function myTenantId(Request $request): int
    {
        $user = $request->user();
        return (int) ($user->tenant_id ?? $request->header('X-Tenant-ID', 1));
    }

    // GET /operator-network/farmed-jobs  (jobs I've farmed OUT)
    public function farmedJobs(Request $request)
    {
        try {
            $myId    = $this->myTenantId($request);
            $status  = $request->get('status');
            $perPage = (int) $request->get('per_page', 20);

            $query = OperatorFarmedJob::with(['toTenant', 'senderBooking', 'receiverBooking'])
                ->where('from_tenant_id', $myId);

            if ($status) {
                $query->where('status', $status);
            }

            $jobs = $query->orderBy('created_at', 'desc')->paginate($perPage);

            return response()->json([
                'success' => true,
                'data'    => collect($jobs->items())->map(fn($j) => $this->mapJob($j, 'out')),
                'pagination' => [
                    'current_page' => $jobs->currentPage(),
                    'total'        => $jobs->total(),
                    'last_page'    => $jobs->lastPage(),
                ],
            ]);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    // GET /operator-network/received-jobs  (jobs farmed TO me)
    public function receivedJobs(Request $request)
    {
        try {
            $myId    = $this->myTenantId($request);
            $status  = $request->get('status');
            $perPage = (int) $request->get('per_page', 20);

            $query = OperatorFarmedJob::with(['fromTenant', 'senderBooking', 'receiverBooking'])
                ->where('to_tenant_id', $myId);

            if ($status) {
                $query->where('status', $status);
            }

            $jobs = $query->orderBy('created_at', 'desc')->paginate($perPage);

            return response()->json([
                'success' => true,
                'data'    => collect($jobs->items())->map(fn($j) => $this->mapJob($j, 'in')),
                'pagination' => [
                    'current_page' => $jobs->currentPage(),
                    'total'        => $jobs->total(),
                    'last_page'    => $jobs->lastPage(),
                ],
            ]);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    // POST /operator-network/farmed-jobs
    public function store(Request $request)
    {
        $v = Validator::make($request->all(), [
            'to_tenant_id'    => 'required|integer|exists:tenants,id',
            'passenger_name'  => 'required|string|max:255',
            'pickup_address'  => 'required|string|max:500',
            'dropoff_address' => 'required|string|max:500',
            'pickup_datetime' => 'required|date',
            'agreed_rate'     => 'required|numeric|min:0',
            'vehicle_type'    => 'nullable|string|max:100',
            'booking_ref'     => 'nullable|string|max:100',
            'passenger_phone' => 'nullable|string|max:50',
            'currency'        => 'nullable|string|max:10',
            'notes'           => 'nullable|string|max:1000',
        ]);
        if ($v->fails()) {
            return response()->json(['success' => false, 'errors' => $v->errors()], 422);
        }

        try {
            $myId = $this->myTenantId($request);

            $result = DB::transaction(function () use ($request, $myId) {
                // 1. Create the farmed job
                $job = OperatorFarmedJob::create([
                    'from_tenant_id'  => $myId,
                    'to_tenant_id'    => $request->to_tenant_id,
                    'partnership_id'  => $request->partnership_id,
                    'booking_ref'     => $request->booking_ref,
                    'passenger_name'  => $request->passenger_name,
                    'passenger_phone' => $request->passenger_phone,
                    'pickup_address'  => $request->pickup_address,
                    'dropoff_address' => $request->dropoff_address,
                    'pickup_datetime' => $request->pickup_datetime,
                    'vehicle_type'    => $request->get('vehicle_type', 'Sedan'),
                    'agreed_rate'     => $request->agreed_rate,
                    'currency'        => $request->get('currency', 'USD'),
                    'notes'           => $request->notes,
                ]);

                // 2. Auto-create a Booking in the SENDER tenant's table
                $senderBooking = $this->createBookingForJob($job, $myId, 'Farm-out');

                // 3. Link job ↔ sender booking
                $job->update([
                    'booking_id'  => $senderBooking->id,
                    // Use provided booking_ref or fall back to the generated booking number
                    'booking_ref' => $job->booking_ref ?: $senderBooking->booking_number,
                ]);
                $senderBooking->update(['farmed_job_id' => $job->id]);

                return [$job->fresh(), $senderBooking];
            });

            [$job, $senderBooking] = $result;

            return response()->json([
                'success'        => true,
                'data'           => $this->mapJob($job, 'out'),
                'booking_number' => $senderBooking->booking_number,
                'message'        => "Farm-out job created. Booking {$senderBooking->booking_number} added to your dispatch queue.",
            ], 201);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    // PATCH /operator-network/farmed-jobs/{id}/cancel
    public function cancel(Request $request, int $id)
    {
        try {
            $myId = $this->myTenantId($request);
            $job  = OperatorFarmedJob::where('id', $id)->where('from_tenant_id', $myId)
                ->whereIn('status', ['pending_acceptance', 'accepted'])->firstOrFail();

            DB::transaction(function () use ($job) {
                $job->update(['status' => 'cancelled']);

                // Cancel both linked bookings so dispatchers see the cancellation
                if ($job->booking_id) {
                    Booking::where('id', $job->booking_id)->update(['status' => 'canceled']);
                }
                if ($job->receiver_booking_id) {
                    Booking::where('id', $job->receiver_booking_id)->update(['status' => 'canceled']);
                }
            });

            return response()->json(['success' => true, 'message' => 'Job cancelled']);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    // PATCH /operator-network/received-jobs/{id}/accept
    public function accept(Request $request, int $id)
    {
        try {
            $myId = $this->myTenantId($request);
            $job  = OperatorFarmedJob::where('id', $id)->where('to_tenant_id', $myId)
                ->where('status', 'pending_acceptance')->firstOrFail();

            $result = DB::transaction(function () use ($job, $myId) {
                $job->update(['status' => 'accepted', 'accepted_at' => now()]);

                // Auto-create a Booking in the RECEIVER tenant's table so their dispatcher can manage it
                $receiverBooking = $this->createBookingForJob($job, $myId, 'Farm-in');

                $job->update(['receiver_booking_id' => $receiverBooking->id]);
                $receiverBooking->update(['farmed_job_id' => $job->id]);

                return $receiverBooking;
            });

            return response()->json([
                'success'        => true,
                'booking_number' => $result->booking_number,
                'message'        => "Job accepted. Booking {$result->booking_number} added to your dispatch queue.",
            ]);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    // PATCH /operator-network/received-jobs/{id}/reject
    public function rejectJob(Request $request, int $id)
    {
        try {
            $myId = $this->myTenantId($request);
            $job  = OperatorFarmedJob::where('id', $id)->where('to_tenant_id', $myId)
                ->where('status', 'pending_acceptance')->firstOrFail();

            $job->update([
                'status'           => 'rejected',
                'rejection_reason' => $request->get('reason'),
            ]);
            return response()->json(['success' => true, 'message' => 'Job rejected']);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    // PATCH /operator-network/received-jobs/{id}/assign-driver
    public function assignDriver(Request $request, int $id)
    {
        $v = Validator::make($request->all(), ['driver_name' => 'required|string|max:255']);
        if ($v->fails()) {
            return response()->json(['success' => false, 'errors' => $v->errors()], 422);
        }
        try {
            $myId = $this->myTenantId($request);
            $job  = OperatorFarmedJob::where('id', $id)->where('to_tenant_id', $myId)
                ->where('status', 'accepted')->firstOrFail();

            DB::transaction(function () use ($job, $request) {
                $job->update(['assigned_driver' => $request->driver_name, 'status' => 'in_progress']);

                // Reflect driver assignment in the receiver booking dispatch notes
                if ($job->receiver_booking_id) {
                    Booking::where('id', $job->receiver_booking_id)->update([
                        'dispatch_notes' => "Driver: {$request->driver_name}" .
                            ($job->notes ? "\n{$job->notes}" : ''),
                        'status' => 'driver_assigned',
                    ]);
                }
                // Update sender booking status too
                if ($job->booking_id) {
                    Booking::where('id', $job->booking_id)->update(['status' => 'driver_assigned']);
                }
            });

            return response()->json(['success' => true, 'message' => 'Driver assigned']);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    // PATCH /operator-network/received-jobs/{id}/complete
    public function complete(Request $request, int $id)
    {
        try {
            $myId = $this->myTenantId($request);
            $job  = OperatorFarmedJob::where('id', $id)->where('to_tenant_id', $myId)
                ->where('status', 'in_progress')->firstOrFail();

            DB::transaction(function () use ($job) {
                $job->update(['status' => 'completed', 'completed_at' => now()]);

                // Mark both bookings as completed
                if ($job->receiver_booking_id) {
                    Booking::where('id', $job->receiver_booking_id)->update([
                        'status'       => 'completed',
                        'completed_at' => now(),
                    ]);
                }
                if ($job->booking_id) {
                    Booking::where('id', $job->booking_id)->update([
                        'status'       => 'completed',
                        'completed_at' => now(),
                    ]);
                }
            });

            return response()->json(['success' => true, 'message' => 'Job marked complete']);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    private function mapJob(OperatorFarmedJob $j, string $direction): array
    {
        $partner = $direction === 'out' ? $j->toTenant : $j->fromTenant;
        return [
            'id'                      => $j->id,
            'job_ref'                 => $j->job_ref,
            'direction'               => $direction,
            'partner_name'            => $partner?->name ?? 'Unknown',
            'partner_id'              => $partner?->id,
            'booking_ref'             => $j->booking_ref,
            // Booking numbers for dispatcher visibility
            'booking_number'          => $j->senderBooking?->booking_number,
            'receiver_booking_number' => $j->receiverBooking?->booking_number,
            'passenger_name'          => $j->passenger_name,
            'passenger_phone'         => $j->passenger_phone,
            'pickup_address'          => $j->pickup_address,
            'dropoff_address'         => $j->dropoff_address,
            'pickup_datetime'         => $j->pickup_datetime?->format('Y-m-d H:i'),
            'vehicle_type'            => $j->vehicle_type,
            'agreed_rate'             => $j->agreed_rate,
            'currency'                => $j->currency,
            'status'                  => $j->status,
            'assigned_driver'         => $j->assigned_driver,
            'notes'                   => $j->notes,
            'rejection_reason'        => $j->rejection_reason,
            'created_at'              => $j->created_at?->format('Y-m-d H:i'),
            'accepted_at'             => $j->accepted_at?->format('Y-m-d H:i'),
            'completed_at'            => $j->completed_at?->format('Y-m-d H:i'),
        ];
    }

    /**
     * Auto-create a dispatcher-visible Booking record for a farmed job.
     *
     * @param OperatorFarmedJob $job
     * @param int    $tenantId   Tenant that owns this booking (sender or receiver)
     * @param string $farmOption 'Farm-out' (sender) | 'Farm-in' (receiver)
     */
    private function createBookingForJob(OperatorFarmedJob $job, int $tenantId, string $farmOption): Booking
    {
        $bookingNumber = $this->generateBookingNumber($tenantId);
        $nameParts     = explode(' ', trim($job->passenger_name), 2);

        $booking = Booking::create([
            'tenant_id'        => $tenantId,
            'booking_number'   => $bookingNumber,
            // Passenger identity stored in billing/booked-by fields (no user account required)
            'billing_contact'  => $job->passenger_name,
            'booked_by_first'  => $nameParts[0],
            'booked_by_last'   => $nameParts[1] ?? '',
            // Addresses & schedule
            'pickup_address'   => $job->pickup_address,
            'dropoff_address'  => $job->dropoff_address,
            'scheduled_at'     => $job->pickup_datetime,
            // Service
            'trip_type'        => 'economy',
            'vehicle_type_name'=> $job->vehicle_type,
            // Rates
            'estimated_fare'   => $job->agreed_rate,
            'flat_rate'        => $job->agreed_rate,
            'payment_method'   => 'cash',
            // Farm-out metadata
            'farm_option'      => $farmOption,
            'efarm_status'     => $farmOption === 'Farm-out' ? 'Farmed Out' : 'Farm-in',
            'dispatch_notes'   => $job->notes,
            'status'           => 'pending',
        ]);

        // Create a passenger contact so the booking shows up properly in the dispatcher view
        BookingContact::create([
            'booking_id'   => $booking->id,
            'tenant_id'    => $tenantId,
            'contact_type' => 'booking_contact',
            'first_name'   => $nameParts[0],
            'last_name'    => $nameParts[1] ?? '',
            'email'        => $job->passenger_email ?? '',
            'phone'        => $job->passenger_phone ?? '',
            'is_primary'   => true,
        ]);

        return $booking;
    }
}
