<?php

namespace Modules\Bms\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Modules\Bms\Entities\BmsHall;
use Modules\Bms\Entities\BmsPackage;
use Modules\Bms\Entities\BmsPackageItem;
use Yajra\DataTables\Facades\DataTables;

class PackageController extends BmsBaseController
{
    public function index()
    {
        $this->guard('bms.halls.view');

        if (request()->ajax()) {
            $fnb = $this->fnbLevels();

            $packages = BmsPackage::where('bms_packages.business_id', $this->businessId())
                ->leftJoin('bms_halls', 'bms_halls.id', '=', 'bms_packages.hall_id')
                ->select([
                    'bms_packages.id', 'bms_packages.name', 'bms_packages.fnb_level',
                    'bms_packages.price', 'bms_packages.price_per_person',
                    'bms_packages.is_active', 'bms_halls.name as hall_name',
                ]);

            return DataTables::of($packages)
                ->editColumn('hall_name', fn ($p) => $p->hall_name ?? '<em>'.__('messages.all').'</em>')
                ->editColumn('fnb_level', fn ($p) => $fnb[$p->fnb_level] ?? '—')
                ->editColumn('price', fn ($p) => '<span class="display_currency" data-currency_symbol="true">'.$p->price.'</span>'
                    .($p->price_per_person ? ' <small class="text-muted">/ '.__('bms::lang.attendees').'</small>' : ''))
                ->editColumn('is_active', fn ($p) => $p->is_active
                    ? '<span class="label label-success">'.__('bms::lang.active').'</span>'
                    : '<span class="label label-default">'.__('bms::lang.inactive').'</span>')
                ->addColumn('action', function ($p) {
                    $edit = '<button data-href="'.route('bms.packages.edit', $p->id).'" class="tw-dw-btn tw-dw-btn-xs tw-dw-btn-outline tw-dw-btn-primary bms-modal-btn"><i class="glyphicon glyphicon-edit"></i> '.__('messages.edit').'</button>';
                    $del = '<button data-href="'.route('bms.packages.destroy', $p->id).'" class="tw-dw-btn tw-dw-btn-xs tw-dw-btn-outline tw-dw-btn-error bms-delete"><i class="glyphicon glyphicon-trash"></i> '.__('messages.delete').'</button>';
                    return $edit.' '.$del;
                })
                ->rawColumns(['action', 'is_active', 'price', 'hall_name'])
                ->make(true);
        }

        return view('bms::packages.index');
    }

    public function create()
    {
        $this->guard('bms.packages.manage');
        return view('bms::packages.create', $this->formData());
    }

    public function store(Request $request)
    {
        $this->guard('bms.packages.manage');
        $data = $this->validatedData($request);

        try {
            $hallIds = $this->hallIds($request);
            $package = DB::transaction(function () use ($request, $data, $hallIds) {
                $data['business_id'] = $this->businessId();
                $data['hall_id'] = $hallIds[0] ?? null;
                $data['venue_id'] = $this->resolveVenueId($data['hall_id']);
                $data['is_active'] = $request->boolean('is_active');
                $data['price_per_person'] = $request->boolean('price_per_person');
                $package = BmsPackage::create($data);
                $this->syncHalls($package, $hallIds);
                $this->syncItems($package, $request);
                return $package;
            });
            $this->recompute($hallIds);
            return $this->ok(__('bms::lang.added_success'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    public function edit($id)
    {
        $this->guard('bms.packages.manage');
        $package = BmsPackage::with(['items' => fn ($q) => $q->orderBy('sort_order'), 'halls'])
            ->where('business_id', $this->businessId())->findOrFail($id);
        return view('bms::packages.edit', array_merge($this->formData(), ['package' => $package]));
    }

    public function update(Request $request, $id)
    {
        $this->guard('bms.packages.manage');
        $data = $this->validatedData($request);

        try {
            $hallIds = $this->hallIds($request);
            $affected = DB::transaction(function () use ($request, $data, $id, $hallIds) {
                $package = BmsPackage::where('business_id', $this->businessId())->findOrFail($id);
                $before = $package->halls()->pluck('hall_id')->all();
                $data['hall_id'] = $hallIds[0] ?? null;
                $data['venue_id'] = $this->resolveVenueId($data['hall_id']);
                $data['is_active'] = $request->boolean('is_active');
                $data['price_per_person'] = $request->boolean('price_per_person');
                $package->update($data);
                $this->syncHalls($package, $hallIds);
                $this->syncItems($package, $request);
                return array_unique(array_filter(array_merge($before, $hallIds)));
            });
            $this->recompute($affected);
            return $this->ok(__('bms::lang.updated_success'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    public function destroy($id)
    {
        $this->guard('bms.packages.manage');
        try {
            $package = BmsPackage::with('halls')->where('business_id', $this->businessId())->findOrFail($id);
            $hallIds = $package->halls()->pluck('hall_id')->all();
            $package->items()->delete();
            $package->halls()->detach();
            $package->delete();
            $this->recompute($hallIds);
            return $this->ok(__('bms::lang.deleted_success'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());
            return $this->fail();
        }
    }

    protected function validatedData(Request $request): array
    {
        $data = $this->validate($request, [
            'name'             => 'required|string|max:191',
            'name_ar'          => 'nullable|string|max:191',
            'all_halls'        => 'nullable',
            'hall_ids'         => 'nullable|array',
            'hall_ids.*'       => 'nullable|integer',
            'fnb_level'        => 'nullable|in:high_tea,drinks_only,set_menu,open_buffet,custom',
            'price'            => 'required|numeric|min:0',
            'price_per_person' => 'nullable',
            'min_pax'          => 'nullable|integer|min:0',
            'max_pax'          => 'nullable|integer|min:0',
            'description'      => 'nullable|string',
            'description_ar'   => 'nullable|string',
            'is_active'        => 'nullable',
            'items'              => 'nullable|array',
            'items.*.product_id' => 'nullable|integer',
            'items.*.name'       => 'nullable|string|max:191',
            'items.*.quantity'   => 'nullable|numeric|min:0',
            'items.*.qty_mode'   => 'nullable|in:fixed,per_guest',
            'items.*.unit_cost'  => 'nullable|numeric|min:0',
            'addons'              => 'nullable|array',
            'addons.*.product_id' => 'nullable|integer',
            'addons.*.name'       => 'nullable|string|max:191',
            'addons.*.quantity'   => 'nullable|numeric|min:0',
            'addons.*.qty_mode'   => 'nullable|in:fixed,per_guest',
            'addons.*.unit_cost'  => 'nullable|numeric|min:0',
            'addons.*.addon_price' => 'nullable|numeric|min:0',
        ]);

        // Persisted columns only; halls/items/addons are handled separately.
        unset($data['all_halls'], $data['hall_ids'], $data['items'], $data['addons']);
        return $data;
    }

    /**
     * Resolved hall ids for the package. "All halls" (explicit checkbox, or an
     * empty selection) links every active hall in the business so the package
     * surfaces everywhere; otherwise the chosen, ownership-checked halls.
     */
    protected function hallIds(Request $request): array
    {
        if ($request->boolean('all_halls')) {
            return BmsHall::where('business_id', $this->businessId())->pluck('id')->all();
        }
        $ids = collect($request->input('hall_ids', []))
            ->filter(fn ($v) => $v !== '' && $v !== null)->map('intval')->unique()->values();
        if ($ids->isEmpty()) {
            return BmsHall::where('business_id', $this->businessId())->pluck('id')->all();
        }
        return BmsHall::where('business_id', $this->businessId())->whereIn('id', $ids)->pluck('id')->all();
    }

    protected function syncHalls(BmsPackage $package, array $hallIds): void
    {
        $pivot = collect($hallIds)->mapWithKeys(fn ($id) => [$id => ['business_id' => $this->businessId()]])->all();
        $package->halls()->sync($pivot);
    }

    /**
     * Replace the package's lines from the submitted repeaters: inclusions
     * (items[] — part of the package price) and add-ons (addons[] — optional
     * paid extras with their own qty mode + sale price). Both persist to
     * bms_package_items, distinguished by is_addon.
     */
    protected function syncItems(BmsPackage $package, Request $request): void
    {
        $package->items()->delete();
        $sort = 0;

        foreach ($request->input('items', []) as $row) {
            if (! $this->lineHasContent($row)) {
                continue;
            }
            BmsPackageItem::create(array_merge($this->lineBase($package, $row), [
                'unit_cost'   => $row['unit_cost'] ?? 0,
                'addon_price' => null,
                'is_addon'    => false,
                'sort_order'  => $sort++,
            ]));
        }

        foreach ($request->input('addons', []) as $row) {
            if (! $this->lineHasContent($row)) {
                continue;
            }
            BmsPackageItem::create(array_merge($this->lineBase($package, $row), [
                'unit_cost'   => $row['unit_cost'] ?? 0,
                'addon_price' => (isset($row['addon_price']) && $row['addon_price'] !== '') ? $row['addon_price'] : 0,
                'is_addon'    => true,
                'sort_order'  => $sort++,
            ]));
        }
    }

    private function lineHasContent(array $row): bool
    {
        return ! empty($row['product_id']) || trim($row['name'] ?? '') !== '';
    }

    private function lineBase(BmsPackage $package, array $row): array
    {
        $productId = $row['product_id'] ?? null;
        $name = trim($row['name'] ?? '');
        if ($name === '' && $productId) {
            $name = optional(\App\Product::find($productId))->name ?? ('#'.$productId);
        }
        return [
            'business_id'    => $this->businessId(),
            'package_id'     => $package->id,
            'product_id'     => $productId ?: null,
            'name'           => $name,
            'name_ar'        => $row['name_ar'] ?? null,
            'description'    => $row['description'] ?? null,
            'description_ar' => $row['description_ar'] ?? null,
            'media'          => $this->parseMedia($row['media_text'] ?? ''),
            'options'        => $this->parseOptions($row['options_text'] ?? ''),
            'category'       => $row['category'] ?? null,
            'quantity'       => $row['quantity'] ?? 1,
            'qty_mode'       => ($row['qty_mode'] ?? 'fixed') === 'per_guest' ? 'per_guest' : 'fixed',
            'is_optional'    => false,
        ];
    }

    /** Parse the materials textarea (one "type|url|title" per line) into media. */
    private function parseMedia(string $text): array
    {
        $media = [];
        foreach (preg_split('/\r\n|\r|\n/', $text) as $line) {
            $line = trim($line);
            if ($line === '') {
                continue;
            }
            $parts = array_map('trim', explode('|', $line));
            // A bare URL defaults to an image.
            if (count($parts) === 1) {
                $parts = ['image', $parts[0]];
            }
            $type = in_array($parts[0], ['image', 'video', '360'], true) ? $parts[0] : 'image';
            $url = $parts[1] ?? '';
            if ($url === '') {
                continue;
            }
            $entry = ['type' => $type, 'url' => $url];
            if (! empty($parts[2])) {
                $entry['title'] = $parts[2];
            }
            $media[] = $entry;
        }
        return $media;
    }

    /** Parse the variants textarea (one "label|label_ar|delta" per line). */
    private function parseOptions(string $text): array
    {
        $options = [];
        foreach (preg_split('/\r\n|\r|\n/', $text) as $line) {
            $line = trim($line);
            if ($line === '') {
                continue;
            }
            $parts = array_map('trim', explode('|', $line));
            $label = $parts[0] ?? '';
            if ($label === '') {
                continue;
            }
            $options[] = [
                'label'       => $label,
                'label_ar'    => $parts[1] ?? '',
                'price_delta' => (float) ($parts[2] ?? 0),
            ];
        }
        return $options;
    }

    protected function recompute(array $hallIds): void
    {
        foreach (array_unique(array_filter($hallIds)) as $hid) {
            BmsHall::recomputePricing((int) $hid);
        }
    }

    protected function resolveVenueId($hallId): ?int
    {
        if (empty($hallId)) {
            return null;
        }
        return optional(BmsHall::where('business_id', $this->businessId())->find($hallId))->venue_id;
    }

    protected function formData(): array
    {
        $halls = BmsHall::where('bms_halls.business_id', $this->businessId())
            ->leftJoin('bms_venues', 'bms_venues.id', '=', 'bms_halls.venue_id')
            ->orderBy('bms_venues.name')->orderBy('bms_halls.name')
            ->get(['bms_halls.id', 'bms_halls.name', 'bms_venues.name as venue_name'])
            ->mapWithKeys(fn ($h) => [$h->id => ($h->venue_name ? $h->venue_name.' — ' : '').$h->name])
            ->toArray();

        return [
            'halls'             => $halls,
            'fnbLevels'         => $this->fnbLevels(),
            'productSearchUrl'  => route('bms.packages.product-search'),
            'productPreviewUrl' => route('bms.packages.product-preview', ['id' => '__ID__']),
        ];
    }

    /**
     * select2 server-side product search for the package-items picker. With a
     * 14k+ catalogue we never embed the full list in the form; rows fetch
     * matches on demand, grouped Assets / Buffet-Menus / Products and carrying
     * each product's unit cost so the row can auto-fill it.
     */
    public function productSearch(Request $request)
    {
        $this->guard('bms.packages.manage');
        $term = trim((string) $request->get('q', ''));

        $query = \App\Product::where('business_id', $this->businessId())
            ->whereIn('type', ['single', 'combo', 'variable'])
            ->with(['variations:id,product_id,default_purchase_price,dpp_inc_tax,sell_price_inc_tax']);
        if ($term !== '') {
            $query->where(function ($q) use ($term) {
                $q->where('name', 'like', "%{$term}%")->orWhere('sku', 'like', "%{$term}%");
            });
        }
        $products = $query->orderBy('name')->limit(30)->get(['id', 'name', 'type', 'bms_is_asset']);

        $assets = $combos = $others = [];
        foreach ($products as $p) {
            $v = $p->variations->first();
            $cost = $v ? round((float) ($v->dpp_inc_tax ?: $v->default_purchase_price), 2) : 0.0;
            $sell = $v ? round((float) $v->sell_price_inc_tax, 2) : 0.0;
            $node = ['id' => $p->id, 'text' => $p->name, 'cost' => $cost, 'sell' => $sell];
            if ($p->bms_is_asset) {
                $assets[] = $node;
            } elseif ($p->type === 'combo') {
                $combos[] = $node;
            } else {
                $others[] = $node;
            }
        }

        $results = [];
        if ($assets) { $results[] = ['text' => (string) __('bms::lang.assets'), 'children' => $assets]; }
        if ($combos) { $results[] = ['text' => (string) __('bms::lang.buffet_menus'), 'children' => $combos]; }
        if ($others) { $results[] = ['text' => (string) __('messages.products'), 'children' => $others]; }

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

    /**
     * Read-only "what this item inherits from its linked product" preview for the
     * builder: name/description/media/variants/buffet-contents resolved via the
     * shared ProductPresentation. Lets the author see what the funnel will show
     * when a row's own fields are left blank (single source of truth).
     */
    public function productPreview(Request $request, $id)
    {
        $this->guard('bms.packages.manage');
        $pres = app(\Modules\Bms\Services\Catalog\ProductPresentation::class)
            ->for($this->businessId(), (int) $id);
        if (! $pres) {
            return response()->json(['preview' => null]);
        }

        return response()->json(['preview' => [
            'name'           => $pres['name'],
            'name_ar'        => $pres['name_ar'],
            'description'    => $pres['description'],
            'description_ar' => $pres['description_ar'],
            'media'          => collect($pres['media'])->map(fn ($m) => ['type' => $m['type'] ?? 'image', 'url' => $m['url'] ?? ''])->all(),
            'variants'       => collect($pres['variants'])->map(fn ($v) => [
                'label' => $v['label'] ?? '', 'label_ar' => $v['label_ar'] ?? null, 'price_delta' => (float) ($v['price_delta'] ?? 0),
            ])->all(),
            'contents'       => collect($pres['contents'])->map(fn ($c) => [
                'name' => $c['name'] ?? '', 'name_ar' => $c['name_ar'] ?? null, 'quantity' => (float) ($c['quantity'] ?? 1),
            ])->all(),
        ]]);
    }
}
