<?php

namespace App\Controllers;

use App\Libraries\PhoneNumber;
use App\Models\CorporateAccountModel;
use App\Models\DiningMembershipModel;
use App\Models\DiningTableModel;
use App\Models\GuestModel;
use App\Models\MenuItemModel;
use App\Models\PosOrderItemModel;
use App\Models\PosOrderModel;
use Config\Lookups;
use RuntimeException;

class PosOrderController extends BaseController
{
    public function index()
    {
        return view('pos/orders_index', [
            'pageTitle' => 'Orders',
            'orders'    => (new PosOrderModel())->activeOrders(),
            'lookups'   => new Lookups(),
        ]);
    }

    public function new()
    {
        return view('pos/order_new', [
            'pageTitle' => 'New Order',
            'tables'    => (new DiningTableModel())->withArea()->orderBy('table_number')->findAll(),
            'lookups'   => new Lookups(),
        ]);
    }

    public function create()
    {
        $model = new PosOrderModel();
        $data = [
            'order_type'      => $this->request->getPost('order_type'),
            'dining_table_id' => $this->request->getPost('order_type') === 'dine_in' ? ($this->request->getPost('dining_table_id') ?: null) : null,
            'status'          => 'open',
            'opened_by'       => $this->currentUserId(),
        ];

        if (! $model->insert($data)) {
            return $this->redirectWithModelErrors($model);
        }

        $orderId = $model->getInsertID();
        $this->logActivity('pos', "Opened {$data['order_type']} order #{$orderId}.");

        return redirect()->to('pos/orders/' . $orderId);
    }

    public function show(int $id)
    {
        $order = (new PosOrderModel())->withDetails()->where('pos_orders.id', $id)->first();

        if ($order === null) {
            return redirect()->to('pos/orders')->with('error', 'Order not found.');
        }

        $items = (new PosOrderItemModel())->forOrder($id);

        return view('pos/order_show', [
            'pageTitle'  => 'Order #' . $id,
            'order'      => $order,
            'items'      => $items,
            'total'      => (new PosOrderItemModel())->orderTotal($id),
            'menuGroups' => (new MenuItemModel())->groupedByCategory(),
            'lookups'    => new Lookups(),
        ]);
    }

    public function addItem(int $id)
    {
        $orders = new PosOrderModel();
        $order  = $orders->find($id);

        if ($order === null || $order->status !== 'open') {
            return redirect()->back()->with('error', 'Items can only be added while the order is still open.');
        }

        $menuItem = (new MenuItemModel())->find((int) $this->request->getPost('menu_item_id'));

        if ($menuItem === null) {
            return redirect()->back()->with('error', 'Menu item not found.');
        }

        $items = new PosOrderItemModel();
        $data = [
            'pos_order_id'   => $id,
            'menu_item_id'   => $menuItem->id,
            'item_name'      => $menuItem->name,
            'quantity'       => max(1, (int) $this->request->getPost('quantity')),
            'unit_price'     => $menuItem->price,
            'kitchen_status' => 'pending',
        ];

        if (! $items->insert($data)) {
            return $this->redirectWithModelErrors($items);
        }

        return redirect()->to('pos/orders/' . $id)->with('success', $menuItem->name . ' added.');
    }

    public function sendToKitchen(int $id)
    {
        $orders = new PosOrderModel();

        if ($orders->find($id) === null) {
            return redirect()->to('pos/orders')->with('error', 'Order not found.');
        }

        $orders->sendToKitchen($id);
        $this->logActivity('pos', "Sent order #{$id} to kitchen.");

        return redirect()->to('pos/orders/' . $id)->with('success', 'Sent to kitchen.');
    }

    public function markServed(int $id)
    {
        $orders = new PosOrderModel();

        if ($orders->find($id) === null) {
            return redirect()->to('pos/orders')->with('error', 'Order not found.');
        }

        $orders->markServed($id);
        $this->logActivity('pos', "Marked order #{$id} served.");

        return redirect()->to('pos/orders/' . $id)->with('success', 'Marked served.');
    }

    /**
     * Pay directly: capture or look up the paying guest (same phone-
     * lookup pattern as Phase 1 booking creation), then settle.
     */
    public function settleDirect(int $id)
    {
        $orders = new PosOrderModel();

        if ($orders->find($id) === null) {
            return redirect()->to('pos/orders')->with('error', 'Order not found.');
        }

        $guestId = (int) $this->request->getPost('guest_id');

        if ($guestId <= 0) {
            $guests = new GuestModel();
            $guestData = [
                'full_name' => $this->request->getPost('full_name'),
                'phone'     => $this->request->getPost('phone'),
            ];

            if (empty($guestData['full_name']) || empty($guestData['phone'])) {
                return redirect()->back()->with('error', 'Provide a name and phone for the receipt, or look up an existing guest.');
            }

            if (! $guests->insert($guestData)) {
                return $this->redirectWithModelErrors($guests);
            }

            $guestId = $guests->getInsertID();
        }

        $corporateAccountId = (int) $this->request->getPost('corporate_account_id') ?: null;

        try {
            $folioId = $orders->settleDirect($id, $guestId, $this->request->getPost('method'), $this->currentUserId(), $corporateAccountId);
        } catch (RuntimeException $e) {
            return redirect()->back()->with('error', $e->getMessage());
        }

        $this->logActivity('pos', "Settled order #{$id} directly (folio #{$folioId}).");
        $this->sendPostDiningFeedbackRequest($guestId);

        return redirect()->to('pos/orders')->with('success', 'Order settled.');
    }

    /** Charge to room: guest must be an in-house, checked-in guest. */
    public function settleToRoom(int $id)
    {
        $orders  = new PosOrderModel();
        $guestId = (int) $this->request->getPost('guest_id');

        if ($orders->find($id) === null) {
            return redirect()->to('pos/orders')->with('error', 'Order not found.');
        }

        if ($guestId <= 0) {
            return redirect()->back()->with('error', 'Look up the in-house guest by phone first.');
        }

        try {
            $folioId = $orders->settleToRoom($id, $guestId, $this->currentUserId());
        } catch (RuntimeException $e) {
            return redirect()->back()->with('error', $e->getMessage());
        }

        $this->logActivity('pos', "Charged order #{$id} to room (folio #{$folioId}).");
        $this->sendPostDiningFeedbackRequest($guestId);

        return redirect()->to('pos/orders')->with('success', 'Charged to room.');
    }

    /**
     * Same 'post_stay_feedback' message type CheckOutController/
     * RestaurantReservationController already use — the settle flow
     * always has a real guest_id by this point (settleDirect creates one
     * if needed, settleToRoom requires an existing checked-in guest), so
     * this fires on every settled order, dine-in or takeaway.
     */
    private function sendPostDiningFeedbackRequest(int $guestId): void
    {
        $guest = (new GuestModel())->find($guestId);

        if ($guest === null || empty($guest->phone)) {
            return;
        }

        service('messagingService')->send(
            'post_stay_feedback',
            $guest->id,
            $guest->phone,
            "Hi {$guest->full_name}, thank you for dining with us! We'd love to hear about your experience — please reply and let us know how it was.",
            [$guest->full_name],
        );
    }

    /**
     * Same reasoning as lookupGuest() below — scoped to POS so fnb
     * doesn't need the manager/front_desk-only /corporate-accounts area
     * just to bill a walk-in F&B order to a company.
     */
    public function lookupCorporateAccount()
    {
        $term = (string) $this->request->getGet('q');

        if (trim($term) === '') {
            return $this->response->setJSON(['results' => []]);
        }

        $accounts = (new CorporateAccountModel())->search($term)->limit(10)->findAll();

        return $this->response->setJSON([
            'results' => array_map(static fn ($a) => ['id' => $a->id, 'company_name' => $a->company_name], $accounts),
        ]);
    }

    /** Same phone-lookup pattern as bookings/dining reservations, scoped to POS so fnb doesn't need /guests access. */
    public function lookupGuest()
    {
        $phone      = (string) $this->request->getGet('phone');
        $normalized = PhoneNumber::normalize($phone);

        if ($normalized === null) {
            return $this->response->setJSON(['found' => false]);
        }

        $guest = (new GuestModel())->where('phone', $normalized)->first();

        if ($guest === null) {
            return $this->response->setJSON(['found' => false]);
        }

        $lookups  = new Lookups();
        $segMeta  = $lookups->guestSegments[$guest->segment] ?? null;
        $dining   = (new DiningMembershipModel())->forGuest($guest->id);
        $dineMeta = $dining && $dining->status === 'active' ? ($lookups->diningMembershipTiers[$dining->tier] ?? null) : null;

        return $this->response->setJSON([
            'found' => true,
            'guest' => [
                'id'           => $guest->id,
                'full_name'    => $guest->full_name,
                'phone'        => $guest->phone,
                'segmentLabel' => $segMeta['label'] ?? null,
                'segmentBadge' => $segMeta['badge'] ?? null,
                'diningLabel'  => $dineMeta['label'] ?? null,
                'diningBadge'  => $dineMeta['badge'] ?? null,
            ],
        ]);
    }
}
