<?php

namespace App\Http\Controllers\Operations;

use App\Http\Controllers\Controller;
use App\Models\Branch;
use App\Models\Driver;
use App\Models\Party;
use App\Models\TransportJob;
use App\Models\Trip;
use App\Models\Vehicle;
use App\Services\Documents\DocumentNumberService;
use App\Services\Operations\JobProfitabilityService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

final class TripController extends Controller
{
    public function __construct(private readonly DocumentNumberService $numbers, private readonly JobProfitabilityService $profitability) {}

    public function index(Request $request): View
    {
        $this->permission($request,'view');
        $query=Trip::query()->with(['job.customer','vehicle','driver','vendor','branch'])->where('company_id',$request->user()->company_id);
        $this->scope($request,$query);
        if($search=trim((string)$request->query('search'))){$query->where(fn($q)=>$q->where('trip_number','like',"%{$search}%")->orWhereHas('job',fn($j)=>$j->where('job_number','like',"%{$search}%"))->orWhereHas('vehicle',fn($v)=>$v->where('plate_number','like',"%{$search}%")));}
        if($status=$request->query('status')){$query->where('status',$status);} $trips=$query->latest('pickup_at')->latest('id')->paginate(25)->withQueryString();
        return view('trips.index',compact('trips'));
    }

    public function create(Request $request): View
    {
        $this->permission($request,'create'); $trip=new Trip(['branch_id'=>$request->user()->branch_id,'transport_job_id'=>$request->integer('job_id')?:null,'pickup_at'=>now(),'status'=>'scheduled']);
        return $this->form($request,$trip);
    }

    public function store(Request $request): RedirectResponse
    {
        $this->permission($request,'create'); $data=$this->validated($request); $data += ['company_id'=>$request->user()->company_id];
        $data['trip_number']=$this->numbers->next((int)$data['company_id'],(int)$data['branch_id'],'TRIP',$data['pickup_at']??today());
        $trip=Trip::query()->create($data); $this->profitability->recalculate($trip->job);
        return redirect()->route('trips.show',$trip)->with('success','Trip created.');
    }

    public function show(Request $request,Trip $trip): View { $this->access($request,$trip,'view'); $trip->load(['job.customer','branch','vehicle','driver','vendor','pods','costs']); return view('trips.show',compact('trip')); }
    public function edit(Request $request,Trip $trip): View { $this->access($request,$trip,'edit_draft'); abort_if(in_array($trip->status,['delivered','cancelled'],true),422,'Delivered or cancelled trips cannot be edited.'); return $this->form($request,$trip); }
    public function update(Request $request,Trip $trip): RedirectResponse { $this->access($request,$trip,'edit_draft'); $oldJob=$trip->job; $trip->update($this->validated($request)); $this->profitability->recalculate($oldJob); if((int)$oldJob->id!==(int)$trip->transport_job_id)$this->profitability->recalculate($trip->job); return redirect()->route('trips.show',$trip)->with('success','Trip updated.'); }
    public function destroy(Request $request,Trip $trip): RedirectResponse { $this->access($request,$trip,'delete_draft'); abort_if($trip->pods()->exists()||$trip->costs()->exists(),422,'Trip has linked records and cannot be deleted.'); $job=$trip->job; $trip->delete(); $this->profitability->recalculate($job); return redirect()->route('trips.index')->with('success','Trip deleted.'); }
    public function status(Request $request,Trip $trip): RedirectResponse { $this->access($request,$trip,'edit_draft'); $data=$request->validate(['status'=>['required',Rule::in(['scheduled','vehicle_assigned','driver_assigned','in_transit','reached_destination','delivered','cancelled'])],'pickup_at'=>'nullable|date','delivery_at'=>'nullable|date|after_or_equal:pickup_at']); $trip->update($data); if($data['status']==='delivered' && $trip->job->trips()->where('status','!=','delivered')->doesntExist()){$trip->job->update(['status'=>'delivered','delivery_status'=>'delivered']);} return back()->with('success','Trip status updated.'); }

    private function form(Request $request,Trip $trip): View
    {
        $company=(int)$request->user()->company_id;
        $branches=Branch::query()->where('company_id',$company)->where('is_active',true)->orderBy('code')->get();
        $jobs=TransportJob::query()->where('company_id',$company)->whereNotIn('status',['closed','cancelled'])->latest('job_date')->limit(1000)->get();
        $vehicles=Vehicle::query()->where('company_id',$company)->where('is_active',true)->orderBy('plate_number')->get();
        $drivers=Driver::query()->where('company_id',$company)->where('is_active',true)->orderBy('name')->get();
        $vendors=Party::query()->where('company_id',$company)->where('is_active',true)->whereHas('roles',fn($q)=>$q->whereIn('role',['vendor','subcontractor','vehicle_owner']))->orderBy('legal_name')->get();
        return view('trips.form',compact('trip','branches','jobs','vehicles','drivers','vendors'));
    }

    private function validated(Request $request): array
    {
        $data=$request->validate(['branch_id'=>'required|integer','transport_job_id'=>'required|integer','vehicle_id'=>'nullable|integer','driver_id'=>'nullable|integer','vendor_id'=>'nullable|integer','pickup_at'=>'nullable|date','delivery_at'=>'nullable|date|after_or_equal:pickup_at','pickup_location'=>'nullable|string|max:255','delivery_location'=>'nullable|string|max:255','customer_rate'=>'nullable|numeric|min:0','vendor_rate'=>'nullable|numeric|min:0','additional_revenue'=>'nullable|numeric|min:0','additional_cost'=>'nullable|numeric|min:0','start_odometer'=>'nullable|numeric|min:0','end_odometer'=>'nullable|numeric|gte:start_odometer','status'=>['required',Rule::in(['scheduled','vehicle_assigned','driver_assigned','in_transit','reached_destination','delivered','cancelled'])],'notes'=>'nullable|string|max:2000']);
        $company=(int)$request->user()->company_id;
        $job = TransportJob::query()->where('company_id',$company)->whereKey($data['transport_job_id'])->first();
        abort_unless($job,422,'Invalid job.');
        abort_unless((int) $job->branch_id === (int) $data['branch_id'], 422, 'Trip branch must match the transport job branch.');
        abort_unless(Branch::query()->where('company_id', $company)->whereKey($data['branch_id'])->exists(), 422, 'Invalid branch.');
        if (! empty($data['vehicle_id'])) {
            abort_unless(Vehicle::query()->where('company_id', $company)->whereKey($data['vehicle_id'])->exists(), 422, 'Invalid vehicle.');
        }
        if (! empty($data['driver_id'])) {
            abort_unless(Driver::query()->where('company_id', $company)->whereKey($data['driver_id'])->exists(), 422, 'Invalid driver.');
        }
        if (! empty($data['vendor_id'])) {
            abort_unless(Party::query()->where('company_id', $company)->whereKey($data['vendor_id'])->whereHas('roles', fn ($query) => $query->whereIn('role', ['vendor', 'subcontractor', 'vehicle_owner']))->exists(), 422, 'Invalid vendor.');
        }
        return $data;
    }

    private function permission(Request $r,string $a):void{abort_unless($r->user()->hasPermission('trips.'.$a),403);} private function access(Request $r,Trip $t,string $a):void{$this->permission($r,$a);abort_unless((int)$t->company_id===(int)$r->user()->company_id,404);if($r->user()->branch_id&&!$r->user()->hasPermission('branches.view_all'))abort_unless((int)$t->branch_id===(int)$r->user()->branch_id,403);} private function scope(Request $r,$q):void{if($r->user()->branch_id&&!$r->user()->hasPermission('branches.view_all'))$q->where('branch_id',$r->user()->branch_id);}
}
