<?php

namespace Modules\Vrc\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\Vrc\Entities\VrcRepairOrder;
use Modules\Vrc\Entities\VrcRepairOrderLaborLog;
use Modules\Vrc\Entities\VrcRepairOrderPart;
use Modules\Vrc\Entities\VrcRepairOrderService;
use Modules\Vrc\Entities\VrcService;
use Modules\Vrc\Services\Contracts\ContactServiceContract;
use Modules\Vrc\Services\RepairOrder\InvalidTransitionException;
use Modules\Vrc\Services\RepairOrder\RepairOrderService;
use Modules\Vrc\Services\RepairOrder\TransitionGuardException;
use Yajra\DataTables\Facades\DataTables;

class RepairOrderController extends VrcBaseController
{
    public function __construct(
        protected RepairOrderService $orders,
        protected ContactServiceContract $contacts
    ) {
    }

    /** Board (status columns) + DataTables JSON. */
    public function index()
    {
        $this->guard('vrc.repair_order.view');

        if (request()->ajax()) {
            $rows = VrcRepairOrder::where('vrc_repair_orders.business_id', $this->businessId())
                ->leftJoin('contacts as c', 'c.id', '=', 'vrc_repair_orders.erp_customer_id')
                ->leftJoin('vrc_vehicles as v', 'v.id', '=', 'vrc_repair_orders.vehicle_id')
                ->when(request('status'), fn ($q) => $q->where('vrc_repair_orders.status', request('status')))
                ->select(['vrc_repair_orders.id', 'vrc_repair_orders.number', 'vrc_repair_orders.status', 'vrc_repair_orders.grand_total', 'vrc_repair_orders.promised_at', 'c.name as customer_name', 'v.plate_number']);

            return DataTables::of($rows)
                ->editColumn('number', fn ($r) => $r->number ?: ('#' . $r->id))
                ->editColumn('status', fn ($r) => '<span class="label label-default">' . __('vrc::lang.ro_status_' . $r->status) . '</span>')
                ->editColumn('promised_at', fn ($r) => $r->promised_at ? \Carbon\Carbon::parse($r->promised_at)->format('Y-m-d H:i') : '—')
                ->addColumn('action', fn ($r) => '<a href="' . route('vrc.repair-orders.show', $r->id) . '" class="btn btn-xs btn-primary"><i class="fa fa-folder-open"></i> ' . __('messages.view') . '</a>')
                ->rawColumns(['status', 'action'])
                ->make(true);
        }

        $board = [];
        foreach (['checked_in', 'awaiting_estimate', 'estimate_sent', 'estimate_approved', 'in_progress', 'quality_check', 'ready_for_pickup', 'invoiced'] as $st) {
            $board[$st] = VrcRepairOrder::with(['vehicle', 'customer'])
                ->where('business_id', $this->businessId())->where('status', $st)
                ->orderByDesc('updated_at')->limit(50)->get();
        }

        return view('vrc::repair_orders.index', compact('board'));
    }

    public function create()
    {
        $this->guard('vrc.repair_order.create');
        return view('vrc::repair_orders.create', $this->formData());
    }

    /** Check-in: create the repair order. */
    public function store(Request $request)
    {
        $this->guard('vrc.repair_order.create');
        $data = $this->validate($request, [
            'erp_customer_id'   => 'required|integer',
            'vehicle_id'        => 'required|integer',
            'location_id'       => 'nullable|integer',
            'bay_id'            => 'nullable|integer',
            'priority'          => 'nullable|in:low,normal,high,urgent',
            'odometer_in'       => 'nullable|integer|min:0',
            'fuel_level_in'     => 'nullable|integer|min:0|max:100',
            'customer_concerns' => 'nullable|string',
            'promised_at'       => 'nullable|date',
            'is_rework'         => 'nullable|boolean',
            'rework_of_id'      => 'nullable|integer',
            'rework_reason'     => 'nullable|string|max:255',
        ]);

        try {
            $data['status'] = 'checked_in';
            $data['service_advisor_id'] = $this->userId();

            // Comeback/rework: only honour the link when the flag is set and the
            // referenced prior order belongs to the same business + vehicle.
            $data['is_rework'] = $request->boolean('is_rework');
            if ($data['is_rework'] && ! empty($data['rework_of_id'])) {
                $prior = VrcRepairOrder::where('business_id', $this->businessId())
                    ->where('vehicle_id', $data['vehicle_id'])->find($data['rework_of_id']);
                $data['rework_of_id'] = $prior?->id;
            } else {
                $data['rework_of_id'] = null;
                $data['rework_reason'] = $data['is_rework'] ? ($data['rework_reason'] ?? null) : null;
            }

            $ro = $this->orders->create($this->businessId(), $data, $this->userId());

            return $this->ok(__('vrc::lang.added_success'), ['redirect' => route('vrc.repair-orders.show', $ro->id)]);
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    public function show($id)
    {
        $this->guard('vrc.repair_order.view');
        $ro = VrcRepairOrder::with(['vehicle.make', 'vehicle.model', 'services', 'parts', 'statusHistory', 'bay'])
            ->where('business_id', $this->businessId())->findOrFail($id);
        $customer = $this->contacts->find($this->businessId(), (int) $ro->erp_customer_id);

        return view('vrc::repair_orders.show', array_merge(
            compact('ro', 'customer'),
            [
                'allowed'     => $this->orders->machine()->allowedFrom($ro->status),
                'services'    => VrcService::where('business_id', $this->businessId())->where('is_active', true)->orderBy('name')->get(),
                'technicians' => \App\User::forDropdown($this->businessId(), false),
                'taxRates'    => \App\TaxRate::forBusinessDropdown($this->businessId(), true)['tax_rates'],
                'invoiceStatus' => $ro->erp_invoice_id
                    ? app(\Modules\Vrc\Services\Contracts\InvoiceServiceContract::class)->statusForOrder($ro)
                    : null,
            ]
        ));
    }

    /** JSON: prior repair orders for a vehicle (to pick the original on a comeback). */
    public function priorOrders($vehicleId)
    {
        $this->guard('vrc.repair_order.create');

        $orders = VrcRepairOrder::where('business_id', $this->businessId())
            ->where('vehicle_id', (int) $vehicleId)
            ->orderByDesc('id')->limit(50)
            ->get(['id', 'number', 'completed_at', 'status']);

        return response()->json($orders->mapWithKeys(fn ($o) => [
            $o->id => ($o->number ?: '#' . $o->id) . ' · ' . __('vrc::lang.ro_status_' . $o->status)
                . ($o->completed_at ? ' · ' . $o->completed_at->format('Y-m-d') : ''),
        ]));
    }

    /**
     * Escalate a flagged comeback into a Non-Conformance Report so corrective
     * action is tracked (ISO 9001 NCR→CAPA loop). Idempotent: re-escalating an
     * already-linked rework just returns its NCR.
     */
    public function escalateRework($id)
    {
        $this->guard('vrc.quality.manage');
        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);

        if (! $ro->is_rework) {
            return back()->with('status', ['success' => 0, 'msg' => __('vrc::lang.rework_not_flagged')]);
        }
        if ($ro->rework_ncr_id) {
            return back()->with('status', ['success' => 1, 'msg' => __('vrc::lang.rework_already_escalated')]);
        }

        try {
            $ncr = \Modules\Vrc\Entities\VrcNcr::create([
                'business_id'     => $ro->business_id,
                'source'          => 'rework',
                'title'           => __('vrc::lang.rework') . ' — ' . ($ro->number ?: '#' . $ro->id),
                'description'     => $ro->rework_reason,
                'severity'        => 'minor',
                'status'          => 'open',
                'repair_order_id' => $ro->id,
                'raised_by'       => $this->userId(),
                'raised_at'       => now(),
            ]);
            $ro->update(['rework_ncr_id' => $ncr->id]);

            return back()->with('status', ['success' => 1, 'msg' => __('vrc::lang.rework_escalated')]);
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return back()->with('status', ['success' => 0, 'msg' => __('messages.something_went_wrong')]);
        }
    }

    /** Status transition endpoint. */
    public function transition(Request $request, $id)
    {
        $this->guard('vrc.repair_order.manage');
        $request->validate(['to' => 'required|string']);

        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);

        try {
            $this->orders->transition($ro, $request->to, $this->userId(), $request->get('reason'));
            return $this->ok(__('vrc::lang.status_updated'), ['status' => $ro->status]);
        } catch (InvalidTransitionException $e) {
            return response()->json($this->fail($e->getMessage()), 409);
        } catch (TransitionGuardException $e) {
            return response()->json($this->fail($e->getMessage()), 422);
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    // ---- Service lines ---------------------------------------------------

    public function storeService(Request $request, $id)
    {
        $this->guard('vrc.repair_order.manage');
        $data = $this->validate($request, [
            'service_id'   => 'nullable|integer',
            'description'  => 'required|string|max:255',
            'labor_hours'  => 'nullable|numeric|min:0',
            'labor_rate'   => 'nullable|numeric|min:0',
            'qty'          => 'nullable|numeric|min:0',
            'unit_price'   => 'nullable|numeric|min:0',
            'discount'     => 'nullable|numeric|min:0',
            'tax_rate_id'  => 'nullable|integer',
        ]);

        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);

        // Carry the HSN/SAC tax code from the catalog service onto the line.
        $hsn = null;
        if (! empty($data['service_id'])) {
            $hsn = optional(\Modules\Vrc\Entities\VrcService::where('business_id', $this->businessId())->find($data['service_id']))->hsn_sac;
        }

        try {
            DB::transaction(function () use ($ro, $data, $request, $hsn) {
                VrcRepairOrderService::create([
                    'business_id'    => $ro->business_id,
                    'repair_order_id' => $ro->id,
                    'service_id'     => $data['service_id'] ?? null,
                    'description'    => $data['description'],
                    'hsn_sac'       => $hsn,
                    'labor_hours'   => $data['labor_hours'] ?? 0,
                    'labor_rate'    => $data['labor_rate'] ?? 0,
                    'qty'           => $data['qty'] ?? 1,
                    'unit_price'    => $data['unit_price'] ?? 0,
                    'discount'      => $data['discount'] ?? 0,
                    'discount_type' => $request->get('discount_type', 'fixed'),
                    'tax_rate_id'   => $data['tax_rate_id'] ?? null,
                ]);
                $this->orders->recomputeTotals($ro);
            });
            return $this->ok(__('vrc::lang.added_success'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    public function destroyService($id, $line)
    {
        $this->guard('vrc.repair_order.manage');
        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);
        try {
            VrcRepairOrderService::where('repair_order_id', $ro->id)->where('id', $line)->delete();
            $this->orders->recomputeTotals($ro);
            return $this->ok(__('vrc::lang.deleted_success'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    /** Apply a package: expand into service lines + recompute. */
    public function applyPackage(Request $request, $id)
    {
        $this->guard('vrc.repair_order.manage');
        $request->validate(['package_id' => 'required|integer']);
        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);

        try {
            $pkg = \Modules\Vrc\Entities\VrcServicePackage::with('items.service')
                ->where('business_id', $this->businessId())->findOrFail($request->package_id);

            DB::transaction(function () use ($ro, $pkg) {
                foreach ($pkg->items as $item) {
                    $svc = $item->service;
                    VrcRepairOrderService::create([
                        'business_id'    => $ro->business_id,
                        'repair_order_id' => $ro->id,
                        'service_id'     => $item->service_id,
                        'description'    => $svc->name ?? '',
                        'labor_hours'    => $svc->default_labor_hours ?? 0,
                        'qty'            => $item->qty,
                        'unit_price'     => $item->override_price ?? ($svc->default_price ?? 0),
                        'tax_rate_id'    => ($svc->taxable ?? true) ? ($svc->tax_rate_id ?? null) : null,
                    ]);
                }
                $this->orders->recomputeTotals($ro);
            });
            return $this->ok(__('vrc::lang.package_applied'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    // ---- Part lines ------------------------------------------------------

    public function storePart(Request $request, $id)
    {
        $this->guard('vrc.repair_order.manage');
        $data = $this->validate($request, [
            'erp_product_id'   => 'nullable|integer',
            'erp_variation_id' => 'nullable|integer',
            'description'      => 'required|string|max:255',
            'qty'             => 'nullable|numeric|min:0',
            'unit_price'      => 'nullable|numeric|min:0',
            'unit_cost'       => 'nullable|numeric|min:0',
            'discount'        => 'nullable|numeric|min:0',
            'tax_rate_id'     => 'nullable|integer',
            'source'          => 'nullable|in:stock,special_order,customer_supplied',
            'warranty_months' => 'nullable|integer|min:0',
        ]);

        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);

        try {
            DB::transaction(function () use ($ro, $data, $request) {
                // NOTE: stock consumption (ERP movement) happens in Phase 7 on
                // estimate-approval/consumption, not at line entry.
                VrcRepairOrderPart::create([
                    'business_id'     => $ro->business_id,
                    'repair_order_id' => $ro->id,
                    'erp_product_id'  => $data['erp_product_id'] ?? null,
                    'erp_variation_id' => $data['erp_variation_id'] ?? null,
                    'description'     => $data['description'],
                    'qty'             => $data['qty'] ?? 1,
                    'unit_price'      => $data['unit_price'] ?? 0,
                    'unit_cost'       => $data['unit_cost'] ?? 0,
                    'discount'        => $data['discount'] ?? 0,
                    'discount_type'   => $request->get('discount_type', 'fixed'),
                    'tax_rate_id'     => $data['tax_rate_id'] ?? null,
                    'source'          => $data['source'] ?? 'stock',
                    'warranty_months' => $data['warranty_months'] ?? null,
                ]);
                $this->orders->recomputeTotals($ro);
            });
            return $this->ok(__('vrc::lang.added_success'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    public function destroyPart($id, $line)
    {
        $this->guard('vrc.repair_order.manage');
        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);
        try {
            VrcRepairOrderPart::where('repair_order_id', $ro->id)->where('id', $line)->delete();
            $this->orders->recomputeTotals($ro);
            return $this->ok(__('vrc::lang.deleted_success'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    /** Part picker: search ERP products (live) by text or exact barcode/SKU. */
    public function partsLookup(Request $request)
    {
        $this->guard('vrc.repair_order.manage');
        $q = trim((string) $request->get('q'));
        $barcode = trim((string) $request->get('barcode'));
        $bid = $this->businessId();

        $locationId = (int) request('location_id', 0);

        $variations = \App\Variation::join('products as p', 'p.id', '=', 'variations.product_id')
            ->leftJoin('variation_location_details as vld', function ($j) use ($locationId) {
                $j->on('vld.variation_id', '=', 'variations.id');
                if ($locationId) {
                    $j->where('vld.location_id', '=', $locationId);
                }
            })
            ->where('p.business_id', $bid)
            ->when($barcode !== '', fn ($w) => $w->where('variations.sub_sku', $barcode))
            ->when($barcode === '', fn ($w) => $w->where(function ($x) use ($q) {
                $x->where('p.name', 'like', "%$q%")->orWhere('variations.sub_sku', 'like', "%$q%");
            }))
            ->select([
                'variations.id as variation_id', 'p.id as product_id', 'p.name',
                'variations.sub_sku', 'variations.default_sell_price', 'p.enable_stock',
                \DB::raw('COALESCE(SUM(vld.qty_available), 0) as qty_available'),
            ])
            ->groupBy('variations.id', 'p.id', 'p.name', 'variations.sub_sku', 'variations.default_sell_price', 'p.enable_stock')
            ->limit(20)->get();

        return response()->json($variations);
    }

    // ---- Technicians & labor --------------------------------------------

    public function assign(Request $request, $id)
    {
        $this->guard('vrc.repair_order.manage');
        $request->validate([
            'line_id'        => 'required|integer',
            'technician_id'  => 'required|integer',
        ]);
        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);

        try {
            VrcRepairOrderService::where('repair_order_id', $ro->id)->where('id', $request->line_id)
                ->update(['assigned_technician_id' => $request->technician_id]);
            return $this->ok(__('vrc::lang.technician_assigned'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    public function laborStart(Request $request, $id)
    {
        $this->guard('vrc.repair_order.manage');
        $request->validate(['technician_id' => 'required|integer']);
        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);

        try {
            VrcRepairOrderLaborLog::create([
                'business_id'            => $ro->business_id,
                'repair_order_id'        => $ro->id,
                'repair_order_service_id' => $request->get('line_id'),
                'technician_id'          => $request->technician_id,
                'started_at'             => now(),
            ]);
            return $this->ok(__('vrc::lang.labor_started'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    public function laborStop(Request $request, $id)
    {
        $this->guard('vrc.repair_order.manage');
        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);

        try {
            $log = VrcRepairOrderLaborLog::where('repair_order_id', $ro->id)
                ->whereNull('ended_at')
                ->when($request->get('technician_id'), fn ($q) => $q->where('technician_id', $request->technician_id))
                ->latest('started_at')->first();
            if ($log) {
                $end = now();
                $log->update([
                    'ended_at'     => $end,
                    'duration_min' => $log->started_at->diffInMinutes($end),
                ]);
            }
            return $this->ok(__('vrc::lang.labor_stopped'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    /** Mark a service line done/in_progress/skipped. */
    public function serviceStatus(Request $request, $id, $line)
    {
        $this->guard('vrc.repair_order.manage');
        $request->validate(['status' => 'required|in:pending,in_progress,done,skipped']);
        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);
        try {
            VrcRepairOrderService::where('repair_order_id', $ro->id)->where('id', $line)
                ->update(['status' => $request->status]);
            return $this->ok(__('vrc::lang.updated_success'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    public function timeline($id)
    {
        $this->guard('vrc.repair_order.view');
        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);
        return response()->json($ro->statusHistory()->get());
    }

    /** AI estimate assistant: symptom → suggested causes/services/labor (JSON). */
    public function aiAssist(Request $request, $id)
    {
        $this->guard('vrc.repair_order.manage');
        $ro = VrcRepairOrder::with('vehicle.make', 'vehicle.model')
            ->where('business_id', $this->businessId())->findOrFail($id);

        $result = app(\Modules\Vrc\Services\Ai\VrcEstimateAssistant::class)
            ->suggest($this->businessId(), (string) $request->get('symptom'), $ro->vehicle);

        return response()->json($result);
    }

    /** Compatible parts for this RO's vehicle, via ProductCompat (JSON). */
    public function suggestedParts($id)
    {
        $this->guard('vrc.repair_order.manage');
        $ro = VrcRepairOrder::with('vehicle.make', 'vehicle.model')
            ->where('business_id', $this->businessId())->findOrFail($id);

        $parts = $ro->vehicle
            ? app(\Modules\Vrc\Services\Compat\VrcPartSuggester::class)->forVehicle($this->businessId(), $ro->vehicle)
            : [];

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

    /** Printable job card (PDF) via the ERP mPDF mechanism. */
    public function jobCardPdf($id)
    {
        $this->guard('vrc.repair_order.view');
        $ro = VrcRepairOrder::with(['vehicle.make', 'vehicle.model', 'services', 'parts'])
            ->where('business_id', $this->businessId())->findOrFail($id);
        $customer = $this->contacts->find($this->businessId(), (int) $ro->erp_customer_id);

        $html = view('vrc::repair_orders.job_card_pdf', compact('ro', 'customer'))->render();
        $mpdf = $this->getMpdf();
        $mpdf->WriteHTML($html);

        return $mpdf->Output(($ro->number ?: 'job-card') . '.pdf', 'I');
    }

    /** Generate the ERP invoice from the order and move it to `invoiced`. */
    public function invoice($id)
    {
        $this->guard('vrc.repair_order.manage');
        $ro = VrcRepairOrder::where('business_id', $this->businessId())->findOrFail($id);

        try {
            $invoiceId = app(\Modules\Vrc\Services\Contracts\InvoiceServiceContract::class)
                ->generateFromRepairOrder($ro, $this->userId());
            $ro->refresh();
            if ($this->orders->machine()->canTransition($ro->status, 'invoiced')) {
                $ro->load('services');
                $this->orders->transition($ro, 'invoiced', $this->userId(), 'Invoice generated');
            }

            return $this->ok(__('vrc::lang.invoice_generated'), [
                'erp_invoice_id' => $invoiceId,
                'redirect'       => $invoiceId ? url('sells/' . $invoiceId) : null,
            ]);
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail($e->getMessage());
        }
    }

    protected function formData(): array
    {
        $bid = $this->businessId();
        return [
            'customers' => $this->contacts->customersDropdown($bid),
            'locations' => $this->locations(),
            'bays'      => \Modules\Vrc\Entities\VrcBay::where('business_id', $bid)->where('is_active', true)->pluck('name', 'id')->toArray(),
            'vehicles'  => [],
        ];
    }
}
