<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreProposalRequest;
use App\Http\Requests\UpdateProposalRequest;
use App\Models\Product;
use App\Models\Proposal;
use App\Models\ProposalApproval;
use App\Models\ProposalHistory;
use App\Models\RoleDiscountLimit;
use App\Models\LiftProductVariant;
use App\Models\Upgradation;
use App\Models\Country;
use App\Models\State;
use App\Models\District;
use App\Models\City;
use App\Models\User;
use App\Services\ProposalApprovalService;
use App\Services\ProposalConversionService;
use App\Services\ProposalPdfService;
use App\Services\SmsOtpService;
use App\Models\Client;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

class ProposalController extends Controller
{
    protected $approvalService;
    protected $conversionService;
    protected $pdfService;
    protected $smsOtpService;

    public function __construct(
        ProposalApprovalService $approvalService,
        ProposalConversionService $conversionService,
        ProposalPdfService $pdfService,
        SmsOtpService $smsOtpService
    ) {
        $this->approvalService = $approvalService;
        $this->conversionService = $conversionService;
        $this->pdfService = $pdfService;
        $this->smsOtpService = $smsOtpService;
    }

    /**
     * Display a listing of proposals with visibility filtering
     */
    // public function index()
    // {
    //     $user = Auth::user();
    //     $query = Proposal::with('product', 'createdBy', 'currentHandler');

    //     // Apply visibility filters based on user role
    //     switch ($user->role->slug) {
    //         case 'vp':
    //             // VP can see all proposals
    //             break;
    //         case 'sd':
    //             // SD can see proposals from their state
    //             $query->where('state_id', $user->state_id);
    //             break;
    //         case 'rm':
    //             // RM can see proposals from their district
    //             $query->where('district_id', $user->district_id);
    //             break;
    //         case 'bdm':
    //             // BDM can see proposals from their city
    //             $query->where('city_id', $user->city_id);
    //             break;
    //         case 'bde':
    //             // BDE can see only their own proposals
    //             $query->where('created_by', $user->id);
    //             break;
    //     }

    //     $proposals = $query->paginate(15);

    //     return view('proposals.index', compact('proposals'));
    // }
    public function index()
    {
        $user = Auth::user();

        $query = Proposal::with([
            'product',
            'client',
            'createdBy',
            'currentHandler',
        ]);

        /*
    |--------------------------------------------------------------------------
    | VP
    |--------------------------------------------------------------------------
    */

        if ($user->role->slug != 'vp' && $user->role->slug != 'sp') {

            /*
        |--------------------------------------------------------------------------
        | Hierarchy Users
        |--------------------------------------------------------------------------
        */

            $subordinateIds =
                $user->getDescendantIds();

            $allowedIds =
                array_merge(
                    [$user->id],
                    $subordinateIds
                );

            // Show proposals created by self/subordinates OR assigned to this user as current handler
            $query->where(function ($q) use ($allowedIds, $user) {
                $q->whereIn('created_by', $allowedIds)
                    ->orWhere('current_handler_id', $user->id);
            });
        }

        /*
    |--------------------------------------------------------------------------
    | Geography Filter
    |--------------------------------------------------------------------------
    */

        if ($user->role->slug == 'sd') {

            $query->where(
                'state_id',
                $user->state_id
            );
        }

        if ($user->role->slug == 'rm') {

            $query->where(
                'district_id',
                $user->district_id
            );
        }

        if ($user->role->slug == 'bdm') {

            $query->where(
                'district_id', // city removed, BDM scoped to district
                $user->district_id
            );
        }

        if ($user->role->slug == 'bde') {

            $query->where(
                'created_by',
                $user->id
            );
        }

        $proposals = $query
            ->latest()
            ->get();

        return view(
            'proposals.index',
            compact('proposals')
        );
    }

    /**
     * Show the form for creating a new proposal
     */
    public function create()
    {
        $products     = Product::where('status', true)->get();
        $upgradations = Upgradation::orderBy('id')->get()->groupBy('group_type');

        $user     = Auth::user();
        $country  = $user->country;
        $state    = $user->state;
        $district = $user->district;
        $city     = $user->city;
        $countries = Country::where('name', 'India')->orderBy('name')->get();

        return view('proposals.create', compact(
            'products',
            'upgradations',
            'country',
            'state',
            'district',
            'city',
            'countries'
        ));
    }

    /**
     * Store a newly store proposal
     */

    public function store(Request $request)
    {
        // echo "<pre>";
        // print_r($request->toArray());
        // exit;
        $phoneCountryCode = $request->input('phone_country_code', '+91');
        $phoneDigits = preg_replace('/\D+/', '', (string) $request->phone);
        $alternatePhoneDigits = preg_replace('/\D+/', '', (string) $request->alternate_phone);

        if ($phoneCountryCode === '+91' && strlen($phoneDigits) === 12 && str_starts_with($phoneDigits, '91')) {
            $phoneDigits = substr($phoneDigits, 2);
        }

        if (strlen($alternatePhoneDigits) === 12 && str_starts_with($alternatePhoneDigits, '91')) {
            $alternatePhoneDigits = substr($alternatePhoneDigits, 2);
        }

        $request->merge([
            'phone_country_code' => $phoneCountryCode,
            'phone' => $phoneDigits,
            'alternate_phone' => $alternatePhoneDigits ?: null,
            'gst_number' => $request->filled('gst_number') ? strtoupper(preg_replace('/[^0-9A-Z]/i', '', (string) $request->gst_number)) : null,
        ]);

        $composeAddress = function (string $prefix) use ($request): string {
            $country = Country::find($request->input("{$prefix}_country_id"))?->name;
            $state = DB::table('proposal_address_states')
                ->where('id', $request->input("{$prefix}_state_id"))
                ->value('name');
            $district = DB::table('proposal_address_districts')
                ->where('id', $request->input("{$prefix}_district_id"))
                ->value('name');
            $city = $request->input("{$prefix}_city_id");
            $pincode = $request->input("{$prefix}_pincode");
            $landmark = $request->input("{$prefix}_landmark");

            return collect([
                $request->input("{$prefix}_address1"),
                $request->input("{$prefix}_address2"),
                $request->input("{$prefix}_area"),
                $city,
                $district,
                $state,
                $country,
                $pincode ? 'Pincode: ' . $pincode : null,
                $landmark ? 'Landmark: ' . $landmark : null,
            ])->filter()->implode(', ');
        };

        $request->merge([
            'billing_address' => $composeAddress('billing'),
            'site_address' => $composeAddress('site'),
            'pincode' => $request->input('billing_pincode'),
            'country_id' => $request->input('billing_country_id'),
            'state_id' => Auth::user()->state_id,
            'district_id' => Auth::user()->district_id,
            'city_id' => Auth::user()->city_id,
        ]);

        // Role-based discount max limit
        $roleSlug = Auth::user()->role->slug ?? 'bde';
        $discountMax = $this->maxDiscountAmountForRole(Auth::user()->role_id, $roleSlug);

        $request->validate([
            // Client
            'salutation'      => 'nullable|in:Mr.,Mrs.,Ms.,Dr.',
            'contact_person'  => 'required|string|max:255',
            'phone_country_code' => ['required', 'regex:/^\+[0-9]{1,4}$/'],
            'phone'           => ['required', 'regex:/^[0-9]{4,15}$/'],
            'alternate_phone' => ['nullable', 'regex:/^[6-9][0-9]{9}$/'],
            'gst_number' => ['nullable', 'regex:/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/'],
            'billing_address' => 'required|string|min:10',
            'site_address'    => 'required|string|min:10',
            'billing_address1' => 'required|string|max:255',
            'billing_address2' => 'nullable|string|max:255',
            'billing_area' => 'nullable|string|max:255',
            'billing_country_id' => 'required|exists:countries,id',
            'billing_state_id' => 'required|exists:proposal_address_states,id',
            'billing_district_id' => 'required|exists:proposal_address_districts,id',
            'billing_city_id' => 'required|string|max:255',
            'billing_pincode' => ['required', 'regex:/^[0-9]{6}$/'],
            'billing_landmark' => 'nullable|string|max:255',
            'site_address1' => 'required|string|max:255',
            'site_address2' => 'nullable|string|max:255',
            'site_area' => 'nullable|string|max:255',
            'site_country_id' => 'required|exists:countries,id',
            'site_state_id' => 'required|exists:proposal_address_states,id',
            'site_district_id' => 'required|exists:proposal_address_districts,id',
            'site_city_id' => 'required|string|max:255',
            'site_pincode' => ['required', 'regex:/^[0-9]{6}$/'],
            'site_landmark' => 'nullable|string|max:255',

            // Proposal
            'product_id'      => 'required|exists:lift_products,id',
            'motor_type'      => 'required|in:single_phase,3_phase',
            'luxe_package'    => 'required|in:yes,no',
            'upgradation_ids.0' => 'required_if:luxe_package,yes',
            'upgradation_group.door' => 'required',
            'upgradation_group.color' => 'required_if:luxe_package,no',
            'upgradation_group.swing' => 'required_if:luxe_package,no',
            'upgradation_group.cop' => 'required_if:luxe_package,no',
            'upgradation_group.extra_travel_height' => 'required',
            'upgradation_group.sos' => 'required',
            'luxe_color'      => 'required_if:luxe_package,yes',
            'luxe_swing'      => 'required_if:luxe_package,yes',
            'luxe_cop'        => 'required_if:luxe_package,yes',
            'requested_price' => 'required|numeric|min:1',
            'door_count'      => 'required|integer|min:0|max:4',
            'no_of_floor'     => 'required|integer',
            'installation_cost' => 'nullable|numeric|min:0',
            'transport_cost' => 'nullable|numeric|min:0',
            'unloading_cost' => 'nullable|numeric|min:0',
            'delivery_type'   => 'required|in:standard,fast_track',
            'delivery_term'   => 'required|string',
            'payment_terms_preset' => 'required|in:preset_1,preset_2,preset_3,preset_4',
            'delivery_amount' => 'nullable|numeric|min:0',
            'discount_amount' => "nullable|numeric|min:0|max:{$discountMax}",
            'client_signature' => 'nullable|string',
        ], [
            'discount_amount.max' => "Discount cannot exceed ₹" . number_format($discountMax, 0) . " for " . strtoupper($roleSlug) . " role.",
            'phone_country_code.regex' => 'Please select a valid phone country code.',
            'phone.regex' => 'Enter a valid phone number between 4 and 15 digits.',
            'alternate_phone.regex' => 'Enter a valid alternate 10 digit Indian mobile number starting with 6, 7, 8, or 9.',
            'gst_number.regex' => 'Enter a valid 15 character GST number.',
            'billing_pincode.regex' => 'Billing pincode must be exactly 6 digits.',
            'site_pincode.regex' => 'Site pincode must be exactly 6 digits.',
        ]);

        if ($phoneCountryCode === '+91' && !preg_match('/^[6-9][0-9]{9}$/', $phoneDigits)) {
            throw ValidationException::withMessages([
                'phone' => 'Enter a valid 10 digit Indian mobile number starting with 6, 7, 8, or 9.',
            ]);
        }

        $selectedVariant = LiftProductVariant::where('id', $request->no_of_floor)
            ->where('lift_product_id', $request->product_id)
            ->first();

        if (!$selectedVariant) {
            throw ValidationException::withMessages([
                'no_of_floor' => 'Please select a valid number of floors for the selected product.',
            ]);
        }

        if ((int) $request->door_count > (int) $selectedVariant->number_of_stops) {
            throw ValidationException::withMessages([
                'door_count' => 'Door count cannot exceed the selected number of floors.',
            ]);
        }

        DB::beginTransaction();

        try {

            /*
        |--------------------------------------------------------------------------
        | Product
        |--------------------------------------------------------------------------
        */

            $product = Product::findOrFail($request->product_id);

            // Use product base_price; fallback to submitted original_price
            $originalPrice = $product->base_price > 0
                ? $product->base_price
                : (float) $request->original_price;

            // Prevent division by zero
            if ($originalPrice <= 0) {
                $originalPrice = (float) $request->requested_price;
            }

            $requestedPrice = (float) $request->requested_price;

            /*
        |--------------------------------------------------------------------------
        | Discount Calculation
        |--------------------------------------------------------------------------
        */

            $discountAmount = (float) ($request->discount_amount ?? 0);

            $discountPercentage = $originalPrice > 0
                ? ($discountAmount / $originalPrice) * 100
                : 0;

            /*
        |--------------------------------------------------------------------------
        | GST
        |--------------------------------------------------------------------------
        */

            $installationCost = (float) ($selectedVariant->installation_cost ?? $request->installation_cost ?? 0);
            $transportCost = (float) ($selectedVariant->transport_cost ?? $request->transport_cost ?? 0);
            $unloadingCost = (float) ($selectedVariant->unloading_cost ?? $request->unloading_cost ?? 0);
            $gstPercentage = (float) ($selectedVariant->gst_percentage ?? 0);
            $gstAmount = 0;

            // Grand Total = Base Price + Delivery + Upgrades - Discount
            // (calculated after upgradations are known — set after proposal create)
            $finalAmount    = $requestedPrice;

            /*
        |--------------------------------------------------------------------------
        | Approval Level
        |--------------------------------------------------------------------------
        */

            $approvalLevel = 'bdm';

            if ($discountAmount > 100000 && $discountAmount <= 250000) {

                $approvalLevel = 'rm';
            } elseif ($discountAmount > 250000 && $discountAmount <= 300000) {

                $approvalLevel = 'sd';
            } elseif ($discountAmount > 300000) {

                $approvalLevel = 'vp';
            }

            /*
        |--------------------------------------------------------------------------
        | Create Client
        |--------------------------------------------------------------------------
        */
            $client = Client::create([
                'client_code' => 'CLI-' . time(),
                'company_name' => $request->contact_person,
                'salutation' => $request->salutation,
                'contact_person' => $request->contact_person,
                'phone' => trim($request->phone_country_code . ' ' . $request->phone),
                'alternate_phone' => $request->alternate_phone,
                'email' => $request->email,
                'country_id' => $request->country_id,
                'state_id' => $request->billing_state_id,
                'district_id' => $request->billing_district_id,
                'city_id' => $request->city_id,
                'billing_address' => $request->billing_address,
                'site_address' => $request->site_address,
                'pincode' => $request->pincode,
                'gst_number' => $request->gst_number,
                'assigned_bde_id' => Auth::id(),
                'created_by' => Auth::id(),
                'status' => 'lead',
            ]);
            /*
        |--------------------------------------------------------------------------
        | Create Proposal
        |--------------------------------------------------------------------------
        */
            $upgradationId = null;
            $luxPrice = 0;
            $doorCount = max(0, min(4, (int) ($request->door_count ?? 0)));
            $doorMultiplier = $doorCount + 1;

            $door = $color = $swing = $cop = $extra_travel_height = $sos = null;
            $normaliseUpgradationId = fn($value) => in_array($value, ['nil', ''], true) ? null : $value;

            $doorPrice = $colorPrice = $swingPrice = $copPrice = $extraTravelHeightPrice = $sosPrice = 0;

            if ($request->luxe_package == 'yes') {

                $upgradationId = $request->upgradation_ids[0] ?? null;

                $luxePackage = Upgradation::find($upgradationId);
                $luxPrice = $luxePackage ? (float)$luxePackage->amount : 0;

                $door = $normaliseUpgradationId($request->input('upgradation_group.door'));
                $extra_travel_height = $normaliseUpgradationId($request->input('upgradation_group.extra_travel_height'));

                $doorPrice = (optional(Upgradation::find($door))->amount ?? 0) * $doorMultiplier;
                $extraTravelHeightPrice = optional(Upgradation::find($extra_travel_height))->amount ?? 0;
            } elseif ($request->luxe_package == 'no') {

                $door = $normaliseUpgradationId($request->input('upgradation_group.door'));
                $color = $normaliseUpgradationId($request->input('upgradation_group.color'));
                $swing = $normaliseUpgradationId($request->input('upgradation_group.swing'));
                $cop = $normaliseUpgradationId($request->input('upgradation_group.cop'));
                $extra_travel_height = $normaliseUpgradationId($request->input('upgradation_group.extra_travel_height'));
                $sos = $normaliseUpgradationId($request->input('upgradation_group.sos'));

                $doorPrice = (optional(Upgradation::find($door))->amount ?? 0) * $doorMultiplier;
                $colorPrice = optional(Upgradation::find($color))->amount ?? 0;
                $swingPrice = optional(Upgradation::find($swing))->amount ?? 0;
                $copPrice = optional(Upgradation::find($cop))->amount ?? 0;
                $extraTravelHeightPrice = optional(Upgradation::find($extra_travel_height))->amount ?? 0;
                $sosPrice = optional(Upgradation::find($sos))->amount ?? 0;
            }


            $proposal = Proposal::create([
                'proposal_number' => $this->generateProposalNumber(Auth::user(), (int) $request->state_id),
                'reference_number' => 'REF-' . rand(1000, 9999),
                'client_id' => $client->id,
                'product_id' => $product->id,
                'created_by' => Auth::id(),
                'assigned_bde_id' => Auth::id(),
                'current_handler_id' => Auth::id(),
                'country_id' => $request->country_id,
                'state_id' => $request->state_id,
                'district_id' => $request->district_id,
                'city_id' => $request->city_id,
                'billing_address' => $request->billing_address,
                'site_address' => $request->site_address,
                'same_as_billing_address' => $request->boolean('same_as_billing_address'),
                'billing_country_id' => $request->billing_country_id,
                'billing_state_id' => $request->billing_state_id,
                'billing_district_id' => $request->billing_district_id,
                'billing_city' => $request->billing_city_id,
                'billing_area' => $request->billing_area,
                'billing_address1' => $request->billing_address1,
                'billing_landmark' => $request->billing_landmark,
                'site_country_id' => $request->site_country_id,
                'site_state_id' => $request->site_state_id,
                'site_district_id' => $request->site_district_id,
                'site_city' => $request->site_city_id,
                'site_area' => $request->site_area,
                'site_address1' => $request->site_address1,
                'site_landmark' => $request->site_landmark,
                'original_price' => $originalPrice,
                'requested_price' => $requestedPrice,
                'approved_price' => null,
                'discount_percentage' => 0,
                'discount_amount' => $discountAmount,
                'gst_percentage' => $gstPercentage,
                'gst_amount' => $gstAmount,
                'final_amount' => $finalAmount, // This will be recalculated during approval based on final approved price and GST
                'installation_cost' => $installationCost,
                'transport_cost' => $transportCost,
                'unloading_cost' => $unloadingCost,
                'approval_level' => $approvalLevel,
                'lux_package' => $request->luxe_package ?? null,
                'luxe_package_price' => $luxPrice,
                'door_id' => $door,
                'door_count' => $doorCount,
                'door_price' => $doorPrice,
                'color_id' => $color,
                'color_price' => $colorPrice,
                'swing_id' => $swing,
                'swing_price' => $swingPrice,
                'cop_id' => $cop,
                'cop_price' => $copPrice,
                'extra_travel_height_id' => $extra_travel_height,
                'extra_travel_height_price' => $extraTravelHeightPrice,
                'sos_id' => $sos,
                'sos_price' => $sosPrice,
                'status' => 'pending',
                'payment_terms' => $request->payment_terms,
                'delivery_terms' => $request->delivery_terms,
                'installation_notes' => $request->installation_notes,
                'terms_and_conditions' => $request->terms_and_conditions,
                'no_of_floor' => $request->no_of_floor,
                'motor_type' => $request->motor_type,
                'payment_terms_preset' => $request->payment_terms_preset ?: null,
                'warranty_type'     => $request->warranty_type ?: null,
                'warranty_years'    => $request->warranty_years ?: null,
                'amc_type'          => $request->amc_type ?: null,
                'amc_percentage'    => $request->amc_percentage ?: null,
                'delivery_type'     => $request->delivery_type ?: null,
                'delivery_term'     => $request->delivery_term ?: null,
                'delivery_amount'   => $request->delivery_amount ?? 0,
                'submitted_at' => now(),
            ]);

            /*
        |--------------------------------------------------------------------------
        | Proposal History
        |--------------------------------------------------------------------------
        */

            $history = ProposalHistory::create([
                'proposal_id' => $proposal->id,
                'action_by' => Auth::id(),
                'role_id' => Auth::user()->role_id,
                'action_type' => 'created',
                'old_price' => null,
                'new_price' => $requestedPrice, // will be updated after grand total is calculated
                'old_discount' => null,
                'new_discount' => $discountPercentage,
                'remarks' => 'Proposal created by BDE',
            ]);

            /*
        |--------------------------------------------------------------------------
        | Proposal Upgradations — save as JSON in proposals table
        |--------------------------------------------------------------------------
        */

            $upgradationIds = $request->input('upgradation_ids', []);

            // Collect dropdown group selections
            $radioGroups = $request->input('upgradation_group', []);
            foreach ($radioGroups as $groupType => $selectedId) {
                if (!empty($selectedId)) {
                    $upgradationIds[] = $selectedId;
                }
            }

            $upgradationIds = array_values(array_filter(array_unique($upgradationIds), fn($id) => is_numeric($id)));
            $upgradationTotal = 0;
            $selectedUpgradations = [];

            if (!empty($upgradationIds)) {
                $upgradationRecords = \App\Models\Upgradation::whereIn('id', $upgradationIds)->get();

                foreach ($upgradationRecords as $upg) {
                    $amount = (float) $upg->amount;

                    if (!empty($door) && (int) $upg->id === (int) $door) {
                        $amount *= $doorMultiplier;
                    }

                    $upgradationTotal += $amount;
                    $selectedUpgradations[] = [
                        'id'     => $upg->id,
                        'name'   => $upg->name,
                        'amount' => $amount,
                    ];
                }
            }

            $proposal->update([
                'selected_upgradations' => $selectedUpgradations,
                'upgradation_total'     => $upgradationTotal,
            ]);

            // Save client signature if provided at time of creation
            if (!empty($request->client_signature)) {
                $proposal->update([
                    'client_signature' => $request->client_signature,
                    'signed_at'        => now(),
                    'sign_token'       => \Illuminate\Support\Str::random(48),
                ]);
            }

            /*
        |--------------------------------------------------------------------------
        | Recalculate Final Amount (Grand Total)
        | = Base Price + Delivery Amount + Upgradation Total - Discount Amount
        |--------------------------------------------------------------------------
        */
            $deliveryAmt = (float) ($request->delivery_amount ?? 0);

            $gstApplicableSubtotal = $requestedPrice + $installationCost + $transportCost + $unloadingCost;
            $gstAmount = ($gstApplicableSubtotal * $gstPercentage) / 100;
            $grandTotal = max(0, $gstApplicableSubtotal + $gstAmount + $deliveryAmt + $upgradationTotal - $discountAmount);

            $proposal->update([
                'final_amount'    => $grandTotal,
                'gst_percentage'  => $gstPercentage,
                'gst_amount'      => $gstAmount,
                'delivery_amount' => $deliveryAmt,
            ]);

            // Update history new_price with final grand total
            $history->update(['new_price' => $grandTotal]);

            DB::commit();

            // Initiate approval workflow immediately on create — sets current_handler_id and sends email
            $emailStatus = $this->approvalService->submitProposal($proposal);

            if ($emailStatus === 'sent') {
                $successMsg = 'Proposal created successfully. Email notification sent to the approver.';
            } elseif ($emailStatus === 'failed') {
                $successMsg = 'Proposal created successfully. However, email notification to the approver failed — please inform them manually.';
            } else {
                $successMsg = 'Proposal created successfully.';
            }

            return redirect()
                ->route('proposals.index')
                ->with('success', $successMsg);
        } catch (\Exception $e) {
            DB::rollBack();

            return back()
                ->withInput()
                ->with('error', $e->getMessage());
        }
    }

    /**
     * Display the specified proposal
     */
    public function show(Proposal $proposal)
    {
        // Check visibility
        if (! $this->approvalService->canViewProposal(auth()->user(), $proposal)) {
            abort(403, 'Unauthorized to view this proposal.');
        }

        $this->approvalService->ensureApprovalWorkflow($proposal);

        $proposal->load('client', 'product', 'createdBy', 'currentHandler', 'approvals', 'histories');
        $canApprove = $this->approvalService->canApproveProposal(auth()->user(), $proposal);

        return view('proposals.show', compact('proposal', 'canApprove'));
    }

    /**
     * Download proposal PDF
     */
    public function downloadPdf(Proposal $proposal)
    {
        $proposal->loadMissing('createdBy');
        $proposal->staff_type = $proposal->createdBy?->staff_type ?? 'N/A';

        if (! $this->approvalService->canViewProposal(auth()->user(), $proposal)) {
            abort(403, 'Unauthorized to download this proposal.');
        }

        if ($proposal->proposal_view === false) {
            abort(403, 'Proposal download is disabled.');
        }

        return $this->pdfService->download($proposal);
    }

    public function updateProposalView(Request $request, Proposal $proposal)
    {
        if (auth()->user()->role?->slug !== 'sp') {
            abort(403, 'Only superadmin can update proposal view access.');
        }

        $request->validate([
            'proposal_view' => ['required', 'boolean'],
        ]);

        $hasPendingDownloadOtp = ! $proposal->download_otp_verified_at
            && $proposal->download_otp_hash
            && $proposal->download_otp_expires_at
            && now()->lessThanOrEqualTo($proposal->download_otp_expires_at);

        if ($request->boolean('proposal_view') && $hasPendingDownloadOtp) {
            return redirect()->route('proposals.show', $proposal)
                ->with('error', 'Download cannot be enabled manually while client OTP verification is pending.');
        }

        $proposal->update([
            'proposal_view' => $request->boolean('proposal_view'),
        ]);

        return redirect()->route('proposals.show', $proposal)
            ->with('success', 'Proposal download access updated successfully.');
    }

    public function resendDownloadOtp(Proposal $proposal)
    {
        if (! $this->approvalService->canViewProposal(auth()->user(), $proposal)) {
            abort(403, 'Unauthorized to send OTP for this proposal.');
        }

        if ($proposal->proposal_view) {
            return redirect()->route('proposals.show', $proposal)
                ->with('success', 'Proposal download is already enabled.');
        }

        if ($proposal->status !== 'approved') {
            return redirect()->route('proposals.show', $proposal)
                ->with('error', 'OTP can be sent only for approved proposals.');
        }

        $sent = $this->issueDownloadOtp($proposal);

        return redirect()->route('proposals.show', $proposal)
            ->with('success', $sent
                ? 'OTP sent to client successfully.'
                : 'OTP generated. SMS provider is not configured, so check Laravel logs for the OTP.');
    }

    public function verifyDownloadOtp(Request $request, Proposal $proposal)
    {
        if (! $this->approvalService->canViewProposal(auth()->user(), $proposal)) {
            abort(403, 'Unauthorized to verify OTP for this proposal.');
        }

        $request->validate([
            'otp' => ['required', 'digits:6'],
        ]);

        if (! $proposal->download_otp_hash || ! $proposal->download_otp_expires_at) {
            return redirect()->route('proposals.show', $proposal)
                ->with('error', 'No active OTP found. Please resend OTP.');
        }

        if (now()->greaterThan($proposal->download_otp_expires_at)) {
            return redirect()->route('proposals.show', $proposal)
                ->with('error', 'OTP expired. Please resend OTP.');
        }

        if (! Hash::check($request->otp, $proposal->download_otp_hash)) {
            return redirect()->route('proposals.show', $proposal)
                ->with('error', 'Invalid OTP.');
        }

        $proposal->update([
            'proposal_view' => true,
            'download_otp_hash' => null,
            'download_otp_verified_at' => now(),
        ]);

        return redirect()->route('proposals.show', $proposal)
            ->with('success', 'OTP verified. Proposal download is enabled.');
    }

    /**
     * Convert approved and confirmed proposal into an actual client
     */
    public function convertToClient(Proposal $proposal)
    {
        if (! $this->approvalService->canViewProposal(auth()->user(), $proposal)) {
            abort(403, 'Unauthorized to convert this proposal.');
        }

        request()->validate([
            'customer_confirmed' => ['required', 'accepted'],
        ]);

        if ($proposal->status === 'approved' && $proposal->client_confirmation_status !== 'confirmed') {
            $proposal->update([
                'client_confirmation_status' => 'confirmed',
                'confirmed_at' => now(),
            ]);
        }

        try {
            $this->conversionService->convertToClient($proposal, auth()->user());
        } catch (\Exception $e) {
            return redirect()->back()->with('error', $e->getMessage());
        }

        return redirect()->route('proposals.show', $proposal)
            ->with('success', 'Proposal converted to client successfully.');
    }

    /**
     * Show the form for editing a proposal
     */
    public function edit(Proposal $proposal)
    {
        if ($proposal->status === 'revised') {
            return redirect()->route('proposals.show', $proposal)
                ->with('error', 'Revised proposals are closed. Please use the latest revision record.');
        }

        // Only creator, VP or SP can edit
        if (auth()->id() !== $proposal->created_by && ! in_array(auth()->user()->role->slug, ['vp', 'sp', 'sd'], true)) {
            abort(403, 'Unauthorized to edit this proposal.');
        }

        $products = Product::where('status', true)->get();
        $upgradations = Upgradation::orderBy('id')->get()->groupBy('group_type');
        $user = Auth::user();
        $country = $user->country;
        $state = $user->state;
        $district = $user->district;
        $city = $user->city;
        $countries = Country::where('name', 'India')->orderBy('name')->get();

        $proposal->loadMissing('client', 'product');

        return view('proposals.create', compact(
            'proposal',
            'products',
            'upgradations',
            'country',
            'state',
            'district',
            'city',
            'countries',
        ) + ['isEdit' => true]);
    }

    /**
     * Update the specified proposal
     */
    // public function update(UpdateProposalRequest $request, Proposal $proposal)
    // {
    //     $isSuperAdmin = auth()->user()->role?->slug === 'sp';

    //     if ($proposal->status === 'revised') {
    //         return redirect()->route('proposals.show', $proposal)
    //             ->with('error', 'Revised proposals are closed. Please use the latest revision record.');
    //     }

    //     // Only draft or revision requested proposals can be edited
    //     if (! $isSuperAdmin && ! in_array($proposal->status, ['draft', 'revision_requested'], true)) {
    //         return redirect()->back()->with('error', 'Only draft or revision requested proposals can be edited.');
    //     }

    //     $data = $request->validated();
    //     $proposalData = $this->buildProposalUpdatePayload($proposal, $data);
    //     $shouldCreateRevision = $proposal->status === 'revision_requested'
    //         || ($isSuperAdmin && $proposal->status === 'approved');
    //     $shouldApproveRevision = $isSuperAdmin && $proposal->status === 'approved';

    //     if ($shouldCreateRevision) {
    //         $revision = DB::transaction(function () use ($proposal, $proposalData, $shouldApproveRevision) {
    //             $revisionNumber = $this->nextRevisionNumber($proposal);
    //             $revisionProposalNumber = $this->makeRevisionProposalNumber($proposal, $revisionNumber);
    //             if (preg_match('/-R(\d+)$/', $revisionProposalNumber, $matches)) {
    //                 $revisionNumber = (int) $matches[1];
    //             }

    //             $revision = $proposal->replicate();

    //             $revision->fill($proposalData);
    //             $revision->proposal_number = $revisionProposalNumber;
    //             $revision->revised_from_id = $proposal->revised_from_id ?: $proposal->id;
    //             $revision->revision_number = $revisionNumber;
    //             $revision->status = $shouldApproveRevision ? 'approved' : 'draft';
    //             $revision->proposal_view = $shouldApproveRevision ? false : $revision->proposal_view;
    //             $revision->current_handler_id = $shouldApproveRevision ? null : $proposal->created_by;
    //             $revision->submitted_at = $shouldApproveRevision ? now() : null;
    //             $revision->approved_at = $shouldApproveRevision ? now() : null;
    //             $revision->approved_price = $shouldApproveRevision ? $revision->requested_price : null;
    //             $revision->confirmed_at = null;
    //             $revision->signed_at = $proposal->signed_at;
    //             $revision->sign_token = ! empty($proposal->client_signature)
    //                 ? \Illuminate\Support\Str::random(48)
    //                 : null;
    //             $revision->client_signature = $proposal->client_signature;
    //             $revision->client_confirmation_status = null;
    //             $revision->save();

    //             ProposalHistory::create([
    //                 'proposal_id' => $revision->id,
    //                 'action_by' => auth()->id(),
    //                 'role_id' => auth()->user()?->role_id,
    //                 'action_type' => 'revision_created',
    //                 'old_price' => $proposal->requested_price,
    //                 'new_price' => $revision->requested_price,
    //                 'old_discount' => $proposal->discount_amount,
    //                 'new_discount' => $revision->discount_amount,
    //                 'remarks' => $shouldApproveRevision
    //                     ? "Created and approved revision {$revision->proposal_number} from {$proposal->proposal_number}."
    //                     : "Created revision {$revision->proposal_number} from {$proposal->proposal_number}.",
    //             ]);

    //             $proposal->update([
    //                 'status' => 'revised',
    //                 'current_handler_id' => null,
    //                 'proposal_view' => false,
    //             ]);

    //             ProposalHistory::create([
    //                 'proposal_id' => $proposal->id,
    //                 'action_by' => auth()->id(),
    //                 'role_id' => auth()->user()?->role_id,
    //                 'action_type' => 'revised',
    //                 'old_price' => $proposal->requested_price,
    //                 'new_price' => $revision->requested_price,
    //                 'old_discount' => $proposal->discount_amount,
    //                 'new_discount' => $revision->discount_amount,
    //                 'remarks' => "Closed after creating revision {$revision->proposal_number}.",
    //             ]);

    //             if ($shouldApproveRevision) {
    //                 $this->issueDownloadOtp($revision);
    //             }

    //             return $revision;
    //         });

    //         return redirect()->route('proposals.show', $revision)
    //             ->with('success', "Revision {$revision->proposal_number} created successfully.");
    //     }

    //     $proposal->update($proposalData);

    //     return redirect()->route('proposals.show', $proposal)
    //         ->with('success', 'Proposal updated successfully.');
    // }
    
    public function update(UpdateProposalRequest $request, Proposal $proposal)
    {
        $canManageProposal = in_array(auth()->user()->role?->slug, ['sp', 'vp', 'sd'], true);

        if ($proposal->status === 'revised') {
            return redirect()->route('proposals.show', $proposal)
                ->with('error', 'Revised proposals are closed. Please use the latest revision record.');
        }

        // Only draft or revision requested proposals can be edited
        if (! $canManageProposal && ! in_array($proposal->status, ['draft', 'revision_requested'], true)) {
            return redirect()->back()->with('error', 'Only draft or revision requested proposals can be edited.');
        }

        $data = $request->validated();
        $proposalData = $this->buildProposalUpdatePayload($proposal, $data);
        $shouldCreateRevision = $proposal->status === 'revision_requested'
            || ($canManageProposal && $proposal->status === 'approved');
        $shouldApproveRevision = $canManageProposal && $proposal->status === 'approved';

        if ($shouldCreateRevision) {
            $revision = DB::transaction(function () use ($proposal, $proposalData, $shouldApproveRevision) {
                $revisionNumber = $this->nextRevisionNumber($proposal);
                $revisionProposalNumber = $this->makeRevisionProposalNumber($proposal, $revisionNumber);
                if (preg_match('/-R(\d+)$/', $revisionProposalNumber, $matches)) {
                    $revisionNumber = (int) $matches[1];
                }

                $revision = $proposal->replicate();

                $revision->fill($proposalData);
                $revision->proposal_number = $revisionProposalNumber;
                $revision->revised_from_id = $proposal->revised_from_id ?: $proposal->id;
                $revision->revision_number = $revisionNumber;
                $revision->status = $shouldApproveRevision ? 'approved' : 'draft';
                $revision->proposal_view = $shouldApproveRevision ? false : $revision->proposal_view;
                $revision->current_handler_id = $shouldApproveRevision ? null : $proposal->created_by;
                $revision->submitted_at = $shouldApproveRevision ? now() : null;
                $revision->approved_at = $shouldApproveRevision ? now() : null;
                $revision->approved_price = $shouldApproveRevision ? $revision->requested_price : null;
                $revision->confirmed_at = null;
                $revision->signed_at = $proposal->signed_at;
                $revision->sign_token = ! empty($proposal->client_signature)
                    ? \Illuminate\Support\Str::random(48)
                    : null;
                $revision->client_signature = $proposal->client_signature;
                $revision->client_confirmation_status = null;
                $revision->save();

                ProposalHistory::create([
                    'proposal_id' => $revision->id,
                    'action_by' => auth()->id(),
                    'role_id' => auth()->user()?->role_id,
                    'action_type' => 'revision_created',
                    'old_price' => $proposal->requested_price,
                    'new_price' => $revision->requested_price,
                    'old_discount' => $proposal->discount_amount,
                    'new_discount' => $revision->discount_amount,
                    'remarks' => $shouldApproveRevision
                        ? "Created and approved revision {$revision->proposal_number} from {$proposal->proposal_number}."
                        : "Created revision {$revision->proposal_number} from {$proposal->proposal_number}.",
                ]);

                $proposal->update([
                    'status' => 'revised',
                    'current_handler_id' => null,
                    'proposal_view' => false,
                ]);

                ProposalHistory::create([
                    'proposal_id' => $proposal->id,
                    'action_by' => auth()->id(),
                    'role_id' => auth()->user()?->role_id,
                    'action_type' => 'revised',
                    'old_price' => $proposal->requested_price,
                    'new_price' => $revision->requested_price,
                    'old_discount' => $proposal->discount_amount,
                    'new_discount' => $revision->discount_amount,
                    'remarks' => "Closed after creating revision {$revision->proposal_number}.",
                ]);

                if ($shouldApproveRevision) {
                    $this->issueDownloadOtp($revision);
                }

                return $revision;
            });

            return redirect()->route('proposals.show', $revision)
                ->with('success', "Revision {$revision->proposal_number} created successfully.");
        }

        $proposal->update($proposalData);

        return redirect()->route('proposals.show', $proposal)
            ->with('success', 'Proposal updated successfully.');
    }

    private function nextRevisionNumber(Proposal $proposal): int
    {
        $rootProposalId = $proposal->revised_from_id ?: $proposal->id;
        $currentMaxRevision = (int) Proposal::withTrashed()
            ->where('revised_from_id', $rootProposalId)
            ->max('revision_number');

        return max($currentMaxRevision, (int) $proposal->revision_number) + 1;
    }

    private function makeRevisionProposalNumber(Proposal $proposal, int $revisionNumber): string
    {
        $baseNumber = preg_replace('/-R\d+$/', '', $proposal->proposal_number);
        $revisionNumber = max(1, $revisionNumber);

        do {
            $number = "{$baseNumber}-R{$revisionNumber}";
            $exists = Proposal::withTrashed()
                ->where('proposal_number', $number)
                ->exists();
            if ($exists) {
                $revisionNumber++;
            }
        } while ($exists);

        return $number;
    }

    private function buildProposalUpdatePayload(Proposal $proposal, array $data): array
    {
        $product = Product::find($data['product_id'] ?? $proposal->product_id);

        $originalPrice = (float) ($data['original_price'] ?? $proposal->original_price ?? 0);
        if ($originalPrice <= 0 && $product) {
            $originalPrice = (float) ($product->base_price ?? 0);
        }

        $requestedPrice = (float) ($data['requested_price'] ?? $proposal->requested_price ?? 0);
        $discountAmount = (float) ($data['discount_amount'] ?? $proposal->discount_amount ?? 0);
        $discountPercentage = $originalPrice > 0
            ? ($discountAmount / $originalPrice) * 100
            : (float) ($data['discount_percentage'] ?? $proposal->discount_percentage ?? 0);

        $selectedVariant = ! empty($data['no_of_floor'])
            ? LiftProductVariant::find($data['no_of_floor'])
            : null;
        $gstPercentage = (float) ($data['gst_percentage'] ?? $selectedVariant?->gst_percentage ?? $proposal->gst_percentage ?? 0);
        $installationCost = (float) ($data['installation_cost'] ?? $proposal->installation_cost ?? 0);
        $transportCost = (float) ($data['transport_cost'] ?? $proposal->transport_cost ?? 0);
        $unloadingCost = (float) ($data['unloading_cost'] ?? $proposal->unloading_cost ?? 0);
        $deliveryAmount = (float) ($data['delivery_amount'] ?? $proposal->delivery_amount ?? 0);

        $upgradationPayload = $this->buildUpgradationPayload($proposal, $data);
        $gstApplicableSubtotal = $requestedPrice + $installationCost + $transportCost + $unloadingCost;
        $gstAmount = ($gstApplicableSubtotal * $gstPercentage) / 100;
        $finalAmount = max(0, $gstApplicableSubtotal + $gstAmount + $deliveryAmount + $upgradationPayload['upgradation_total'] - $discountAmount);

        return array_merge([
            'product_id' => $data['product_id'] ?? $proposal->product_id,
            'billing_address' => $this->composeRevisionAddress('billing', $data),
            'site_address' => $this->composeRevisionAddress('site', $data),
            'billing_country_id' => $data['billing_country_id'] ?? $proposal->billing_country_id,
            'billing_state_id' => $data['billing_state_id'] ?? $proposal->billing_state_id,
            'billing_district_id' => $data['billing_district_id'] ?? $proposal->billing_district_id,
            'billing_city' => $data['billing_city_id'] ?? $data['billing_city'] ?? $proposal->billing_city,
            'billing_area' => $data['billing_area'] ?? $proposal->billing_area,
            'billing_address1' => $data['billing_address1'] ?? $proposal->billing_address1,
            'billing_landmark' => $data['billing_landmark'] ?? $proposal->billing_landmark,
            'site_country_id' => $data['site_country_id'] ?? $proposal->site_country_id,
            'site_state_id' => $data['site_state_id'] ?? $proposal->site_state_id,
            'site_district_id' => $data['site_district_id'] ?? $proposal->site_district_id,
            'site_city' => $data['site_city_id'] ?? $data['site_city'] ?? $proposal->site_city,
            'site_area' => $data['site_area'] ?? $proposal->site_area,
            'site_address1' => $data['site_address1'] ?? $proposal->site_address1,
            'site_landmark' => $data['site_landmark'] ?? $proposal->site_landmark,
            'original_price' => $originalPrice,
            'requested_price' => $requestedPrice,
            'discount_amount' => $discountAmount,
            'discount_percentage' => $discountPercentage,
            'gst_percentage' => $gstPercentage,
            'gst_amount' => $gstAmount,
            'final_amount' => $finalAmount,
            'installation_cost' => $installationCost,
            'transport_cost' => $transportCost,
            'unloading_cost' => $unloadingCost,
            'no_of_floor' => $data['no_of_floor'] ?? $proposal->no_of_floor,
            'motor_type' => $data['motor_type'] ?? $proposal->motor_type,
            'warranty_type' => $data['warranty_type'] ?? $proposal->warranty_type,
            'warranty_years' => $data['warranty_years'] ?? $proposal->warranty_years,
            'amc_type' => $data['amc_type'] ?? $proposal->amc_type,
            'amc_percentage' => $data['amc_percentage'] ?? $proposal->amc_percentage,
            'delivery_type' => $data['delivery_type'] ?? $proposal->delivery_type,
            'delivery_term' => $data['delivery_term'] ?? $proposal->delivery_term,
            'delivery_amount' => $deliveryAmount,
            'payment_terms_preset' => $data['payment_terms_preset'] ?? $proposal->payment_terms_preset,
            'payment_terms' => $data['payment_terms'] ?? $proposal->payment_terms,
            'delivery_terms' => $data['delivery_terms'] ?? $proposal->delivery_terms,
            'installation_notes' => $data['installation_notes'] ?? $proposal->installation_notes,
            'terms_and_conditions' => $data['terms_and_conditions'] ?? $proposal->terms_and_conditions,
            'valid_until' => $data['valid_until'] ?? $proposal->valid_until,
        ], $upgradationPayload);
    }

    private function buildUpgradationPayload(Proposal $proposal, array $data): array
    {
        $luxPackage = $data['luxe_package'] ?? $proposal->lux_package;
        $doorCount = (int) ($data['door_count'] ?? $proposal->door_count ?? 0);
        $doorMultiplier = $doorCount + 1;

        $groupData = $data['upgradation_group'] ?? [];

        $ids = [
            'luxe_package' => $luxPackage === 'yes' ? ($data['luxe_package_id'] ?? $data['upgradation_ids'][0] ?? null) : null,
            'door' => $data['door_id'] ?? $groupData['door'] ?? $proposal->door_id,
            'color' => $luxPackage === 'no' ? ($data['color_id'] ?? $groupData['color'] ?? $proposal->color_id) : null,
            'swing' => $luxPackage === 'no' ? ($data['swing_id'] ?? $groupData['swing'] ?? $proposal->swing_id) : null,
            'cop' => $luxPackage === 'no' ? ($data['cop_id'] ?? $groupData['cop'] ?? $proposal->cop_id) : null,
            'extra_travel_height' => $data['extra_travel_height_id'] ?? $groupData['extra_travel_height'] ?? $proposal->extra_travel_height_id,
            'sos' => $data['sos_id'] ?? $groupData['sos'] ?? $proposal->sos_id,
        ];

        $ids = array_map(fn($value) => in_array($value, ['nil', 'yes', ''], true) ? null : $value, $ids);

        $records = Upgradation::whereIn('id', array_filter($ids))->get()->keyBy('id');
        $selected = [];
        $total = 0;

        $amountFor = function (?int $id, bool $multiplyDoor = false) use ($records, $doorMultiplier, &$selected, &$total): float {
            if (! $id || ! isset($records[$id])) {
                return 0;
            }

            $amount = (float) $records[$id]->amount;
            if ($multiplyDoor) {
                $amount *= $doorMultiplier;
            }

            $total += $amount;
            $selected[] = [
                'id' => $records[$id]->id,
                'name' => $records[$id]->name,
                'amount' => $amount,
            ];

            return $amount;
        };

        $luxePackagePrice = $amountFor($ids['luxe_package'] ? (int) $ids['luxe_package'] : null);
        $doorPrice = $amountFor($ids['door'] ? (int) $ids['door'] : null, true);
        $colorPrice = $amountFor($ids['color'] ? (int) $ids['color'] : null);
        $swingPrice = $amountFor($ids['swing'] ? (int) $ids['swing'] : null);
        $copPrice = $amountFor($ids['cop'] ? (int) $ids['cop'] : null);
        $extraTravelHeightPrice = $amountFor($ids['extra_travel_height'] ? (int) $ids['extra_travel_height'] : null);
        $sosPrice = $amountFor($ids['sos'] ? (int) $ids['sos'] : null);

        return [
            'lux_package' => $luxPackage,
            'luxe_package_price' => $luxePackagePrice,
            'door_id' => $ids['door'],
            'door_count' => $doorCount,
            'door_price' => $doorPrice,
            'color_id' => $ids['color'],
            'color_price' => $colorPrice,
            'swing_id' => $ids['swing'],
            'swing_price' => $swingPrice,
            'cop_id' => $ids['cop'],
            'cop_price' => $copPrice,
            'extra_travel_height_id' => $ids['extra_travel_height'],
            'extra_travel_height_price' => $extraTravelHeightPrice,
            'sos_id' => $ids['sos'],
            'sos_price' => $sosPrice,
            'selected_upgradations' => $selected,
            'upgradation_total' => $total,
        ];
    }

    private function composeRevisionAddress(string $prefix, array $data): string
    {
        $country = ! empty($data["{$prefix}_country_id"])
            ? Country::find($data["{$prefix}_country_id"])?->name
            : null;
        $state = ! empty($data["{$prefix}_state_id"])
            ? DB::table('proposal_address_states')->where('id', $data["{$prefix}_state_id"])->value('name')
            : null;
        $district = ! empty($data["{$prefix}_district_id"])
            ? DB::table('proposal_address_districts')->where('id', $data["{$prefix}_district_id"])->value('name')
            : null;

        return collect([
            $data["{$prefix}_address1"] ?? null,
            $data["{$prefix}_area"] ?? null,
            $data["{$prefix}_city_id"] ?? $data["{$prefix}_city"] ?? null,
            $district,
            $state,
            $country,
            $data["{$prefix}_pincode"] ?? null,
            ! empty($data["{$prefix}_landmark"]) ? 'Landmark: ' . $data["{$prefix}_landmark"] : null,
        ])->filter()->implode(', ');
    }


    /**
     * Remove the specified proposal
     */
    public function destroy(Proposal $proposal)
    {
        // Only creator, VP or SP can delete
        if (auth()->id() !== $proposal->created_by && ! in_array(auth()->user()->role->slug, ['vp', 'sp'], true)) {
            abort(403, 'Unauthorized to delete this proposal.');
        }

        // Only draft proposals can be deleted
        if ($proposal->status !== 'draft') {
            return redirect()->back()->with('error', 'Only draft proposals can be deleted.');
        }

        $proposal->delete();

        return redirect()->route('proposals.index')
            ->with('success', 'Proposal deleted successfully.');
    }

    /**
     * Submit proposal for approval
     */
    public function submit(Proposal $proposal)
    {
        if (! in_array($proposal->status, ['draft', 'revision_requested', 'pending'], true)) {
            return redirect()->back()->with('error', 'Only draft or revision requested proposals can be submitted.');
        }

        $emailStatus = $this->approvalService->submitProposal($proposal, [
            'requested_price' => $proposal->requested_price,
            'gst_percentage' => $proposal->gst_percentage,
        ]);

        if ($emailStatus === 'sent') {
            $message = 'Proposal submitted for approval. Email notification sent to the approver.';
        } elseif ($emailStatus === 'failed') {
            $message = 'Proposal submitted for approval. However, email notification to the approver failed — please inform them manually.';
        } else {
            $message = 'Proposal submitted for approval.';
        }

        return redirect()->route('proposals.show', $proposal)
            ->with('success', $message);
    }

    /**
     * Approve proposal (for approvers only)
     */
    public function approve(Request $request, Proposal $proposal)
    {
        if (! $this->approvalService->canApproveProposal(auth()->user(), $proposal)) {
            abort(403, 'You do not have permission to approve this proposal.');
        }

        $request->validate([
            'comments' => ['nullable', 'string'],
            'discount_percentage' => ['nullable', 'numeric', 'min:0', 'max:100'],
        ]);

        $this->approvalService->approveProposal($proposal, auth()->user(), $request->all());

        return redirect()->route('proposals.show', $proposal)
            ->with('success', 'Proposal approved successfully.');
    }

    /**
     * Request proposal revision
     */
    public function revise(Request $request, Proposal $proposal)
    {
        $request->validate([
            'remarks' => ['required', 'string'],
        ]);

        if (auth()->user()->role?->slug === 'sp' && $proposal->status === 'approved') {
            DB::transaction(function () use ($proposal, $request) {
                $proposal->update([
                    'status' => 'revision_requested',
                    'current_handler_id' => $proposal->created_by,
                ]);

                ProposalHistory::create([
                    'proposal_id' => $proposal->id,
                    'action_by' => auth()->id(),
                    'role_id' => auth()->user()?->role_id,
                    'action_type' => 'revision_requested',
                    'old_price' => $proposal->requested_price,
                    'new_price' => $proposal->requested_price,
                    'old_discount' => $proposal->discount_amount,
                    'new_discount' => $proposal->discount_amount,
                    'remarks' => 'Post-approval revision requested by Super Admin: ' . $request->remarks,
                ]);
            });

            return redirect()->route('proposals.show', $proposal)
                ->with('success', 'Revision requested successfully.');
        }

        if (! $this->approvalService->canApproveProposal(auth()->user(), $proposal)) {
            abort(403, 'You do not have permission to request revision for this proposal.');
        }

        $this->approvalService->requestRevision($proposal, auth()->user(), $request->remarks);

        return redirect()->route('proposals.show', $proposal)
            ->with('success', 'Revision requested successfully.');
    }

    /**
     * Request Super Admin approval to revise an already approved proposal.
     */
    public function requestApprovedRevision(Request $request, Proposal $proposal)
    {
        if ($proposal->status !== 'approved') {
            return redirect()->back()->with('error', 'Only approved proposals can be sent for revision approval.');
        }

        if ((int) $proposal->created_by !== (int) auth()->id()) {
            abort(403, 'Only the proposal creator can request post-approval revision.');
        }

        $request->validate([
            'remarks' => ['required', 'string'],
        ]);

        $superAdmin = User::whereHas('role', fn($query) => $query->where('slug', 'sp'))->first();

        if (! $superAdmin) {
            return redirect()->back()->with('error', 'No Super Admin user found to handle this revision request.');
        }

        DB::transaction(function () use ($proposal, $request, $superAdmin) {
            $proposal->update([
                'status' => 'pending_approval',
                'current_handler_id' => $superAdmin->id,
                'approval_level' => 'sp',
            ]);

            ProposalApproval::updateOrCreate(
                [
                    'proposal_id' => $proposal->id,
                    'approval_level' => 'sp',
                    'status' => 'pending',
                ],
                [
                    'approved_by' => null,
                    'comments' => 'Post-approval revision requested: ' . $request->remarks,
                    'approved_discount_percentage' => $proposal->discount_percentage,
                ]
            );

            ProposalHistory::create([
                'proposal_id' => $proposal->id,
                'action_by' => auth()->id(),
                'role_id' => auth()->user()?->role_id,
                'action_type' => 'post_approval_revision_requested',
                'old_price' => $proposal->requested_price,
                'new_price' => $proposal->requested_price,
                'old_discount' => $proposal->discount_amount,
                'new_discount' => $proposal->discount_amount,
                'remarks' => 'Post-approval revision requested to Super Admin: ' . $request->remarks,
            ]);
        });

        return redirect()->route('proposals.show', $proposal)
            ->with('success', 'Revision request sent to Super Admin successfully.');
    }

    /**
     * Move proposal to parent approver
     */
    public function moveParent(Request $request, Proposal $proposal)
    {
        if (! $this->approvalService->canApproveProposal(auth()->user(), $proposal)) {
            abort(403, 'You do not have permission to move this proposal.');
        }

        $request->validate([
            'remarks' => ['nullable', 'string'],
        ]);

        try {
            $this->approvalService->moveToParent($proposal, auth()->user(), $request->remarks);
        } catch (\Exception $e) {
            return redirect()->route('proposals.show', $proposal)
                ->with('error', $e->getMessage());
        }

        return redirect()->route('proposals.show', $proposal)
            ->with('success', 'Proposal escalated to the next approver.');
    }

    /**
     * Reject proposal (for approvers only)
     */
    public function reject(Request $request, Proposal $proposal)
    {
        if (! $this->approvalService->canApproveProposal(auth()->user(), $proposal)) {
            abort(403, 'You do not have permission to reject this proposal.');
        }

        $request->validate([
            'reason' => ['required', 'string'],
        ]);

        $this->approvalService->rejectProposal($proposal, auth()->user(), $request->reason);

        return redirect()->route('proposals.show', $proposal)
            ->with('success', 'Proposal rejected.');
    }

    /**
     * Generate unique proposal number: PROP-{STATE_CODE}-{YEAR}-{SEQUENCE}
     * Example: PROP-TN-2026-0001
     */
    private function generateProposalNumber(?\App\Models\User $user = null, ?int $stateId = null): string
    {
        $year      = now()->format('Y');
        $stateCode = 'XX';

        // Use explicitly passed stateId first, fallback to user's state_id
        $resolvedStateId = $stateId ?? ($user?->state_id);

        if ($resolvedStateId) {
            $state = \App\Models\State::find($resolvedStateId);

            if ($state) {
                if (!empty($state->code)) {
                    $stateCode = strtoupper(trim($state->code));
                } else {
                    $stateCode = strtoupper(
                        substr(preg_replace('/[^A-Za-z]/', '', $state->name), 0, 2)
                    );
                }
            }
        }

        $prefix = "PROP-{$stateCode}-{$year}-";

        // Get max sequence already used for this prefix
        $latest = Proposal::where('proposal_number', 'like', "{$prefix}%")
            ->orderByDesc('proposal_number')
            ->value('proposal_number');

        $nextSeq = 1;
        if ($latest) {
            $parts   = explode('-', $latest);
            $lastSeq = (int) end($parts);
            $nextSeq = $lastSeq + 1;
        }

        // Ensure uniqueness
        do {
            $number = $prefix . str_pad($nextSeq, 4, '0', STR_PAD_LEFT);
            $exists = Proposal::where('proposal_number', $number)->exists();
            if ($exists) $nextSeq++;
        } while ($exists);

        return $number;
    }

    private function maxDiscountAmountForRole(?int $roleId, ?string $roleSlug): float
    {
        return RoleDiscountLimit::maxAmountForRole($roleId, $roleSlug);
    }

    public function getProductVariants($productId)
    {
        $variants = LiftProductVariant::where('lift_product_id', $productId)
            ->select('id', 'variant_name', 'number_of_stops', 'total_travel_height', 'mrp_price', 'gst_percentage', 'installation_cost', 'transport_cost', 'unloading_cost')
            ->get();

        return response()->json($variants);
    }

    private function issueDownloadOtp(Proposal $proposal): bool
    {
        $proposal->loadMissing('client');
        $phone = $proposal->client?->phone;

        if (! $phone) {
            \Log::warning('Proposal download OTP not sent because client phone is missing.', [
                'proposal_id' => $proposal->id,
                'proposal_number' => $proposal->proposal_number,
            ]);

            return false;
        }

        $otp = (string) random_int(100000, 999999);

        $proposal->update([
            'proposal_view' => false,
            'download_otp_hash' => Hash::make($otp),
            'download_otp_sent_at' => now(),
            'download_otp_expires_at' => now()->addMinutes(10),
            'download_otp_verified_at' => null,
        ]);

        return $this->smsOtpService->sendDownloadOtp(
            $phone,
            $otp,
            $proposal->proposal_number,
            $proposal->client?->contact_person,
            $proposal->revision_number
        );
    }

    // =========================================================================
    // E-SIGN METHODS
    // =========================================================================

    /**
     * Generate a unique sign token and return the shareable link
     */
    public function generateSignLink(Proposal $proposal)
    {
        if (! $this->approvalService->canViewProposal(auth()->user(), $proposal)) {
            abort(403);
        }

        // Generate token if not already set
        if (empty($proposal->sign_token)) {
            $proposal->update([
                'sign_token' => \Illuminate\Support\Str::random(48),
            ]);
        }

        $link = route('proposals.sign', $proposal->sign_token);

        return response()->json(['link' => $link]);
    }

    /**
     * Public: Show the e-sign page (no auth required)
     */
    public function showSignPage(string $token)
    {
        $proposal = Proposal::where('sign_token', $token)
            ->with(['client', 'product', 'city', 'createdBy'])
            ->firstOrFail();

        return view('proposals.sign', compact('proposal', 'token'));
    }

    /**
     * Public: Save the client signature
     */
    public function saveSignature(Request $request, string $token)
    {
        $request->validate([
            'signature' => 'required|string', // base64 PNG
        ]);

        $proposal = Proposal::where('sign_token', $token)->firstOrFail();

        // Only save if not already signed
        if (empty($proposal->client_signature)) {
            $proposal->update([
                'client_signature' => $request->signature,
                'signed_at'        => now(),
            ]);
        }

        return response()->json(['success' => true]);
    }

    /**
     * Public: Download the signed PDF
     */
    public function downloadSignedPdf(string $token)
    {
        $proposal = Proposal::where('sign_token', $token)
            ->with(['client', 'product', 'city', 'createdBy'])
            ->firstOrFail();

        if ($proposal->proposal_view === false) {
            abort(403, 'Proposal download is disabled.');
        }

        return $this->pdfService->download($proposal);
    }
}
