<?php

namespace Tenant\Database\Seeds;

use CodeIgniter\Database\Seeder;

/**
 * Populates every module with realistic history — run once by hand against
 * tenant_demo (`php spark db:seed DemoDataEnrichmentSeeder`), not wired into
 * tenant provisioning. DemoHotelSeeder gives a brand-new tenant just enough
 * to log in and try the app; this instead makes an already-provisioned demo
 * tenant look like a hotel that's been live for a couple of months — guests,
 * bookings across the full status range, folios with real charges/payments,
 * housekeeping/maintenance history, a fuller restaurant/menu, rate plans,
 * corporate accounts, inventory, events, and matching activity_log entries
 * so the Activity Log page reads as genuine usage history rather than a
 * pile of raw inserts.
 *
 * Two hard rules, consistent with how every other feature in this app was
 * built this session:
 *  1. Never touch or delete existing rows (Kwame Mensah/Abena Boateng's
 *     real checked-out stays, the live "Yoyo William Simons" booking made
 *     through the actual app today, the existing Acme Ltd event, etc.) —
 *     this only ever adds alongside what's there.
 *  2. Never fabricate a working third-party integration this tenant hasn't
 *     actually configured. hubtel.client_id / hubtel.sms.* / whatsapp.* are
 *     all null in tenant_settings for this tenant, so no payment_attempts
 *     or "sent" message_log rows are added here — a real run through the
 *     app would log those as failed (as the existing 5 message_log rows
 *     already honestly do), and a wall of fabricated "failed" rows doesn't
 *     add anything a demo needs. Rate plans, corporate billing, inventory,
 *     housekeeping/maintenance and folio math are all genuinely internal
 *     features with no such dependency, so those are populated in full.
 */
class DemoDataEnrichmentSeeder extends Seeder
{
    /** @var array<string,int> guest full_name => id */
    private array $guestId = [];

    /** @var array<string,int> room_number => id */
    private array $roomId = [];

    /** @var array<string,int> room_type name => id */
    private array $roomTypeId = [];

    /** @var array<string,array{id:int,price:string}> hotel_services name => [id, price] */
    private array $serviceMeta = [];

    /** @var array<string,int> user email => id */
    private array $userId = [];

    /** @var array<string,int> */
    private array $corporateId = [];

    /** @var array<string,int> */
    private array $tableId = [];

    /** @var array<string,array{id:int,price:string}> */
    private array $menuItemMeta = [];

    private int $manager;
    private int $frontDesk;
    private int $fnb;

    public function run()
    {
        $this->db->transStart();

        $this->loadExistingLookups();
        $this->seedStaff();
        $this->seedRooms();
        $this->seedCorporateAccounts();
        $this->seedRatePlans();
        $this->seedInventory();
        $this->seedDining();
        $this->seedMenu();
        $this->seedGuestsAndBookings();
        $this->seedEventClientsAndEvents();
        $this->seedHousekeeping();
        $this->seedMaintenance();
        $this->seedRestaurantReservations();
        $this->seedPosOrders();
        $this->seedNotifications();

        $this->db->transComplete();
    }

    // ---------------------------------------------------------------
    // Helpers
    // ---------------------------------------------------------------

    private function loadExistingLookups(): void
    {
        foreach ($this->db->table('room_types')->select('id, name')->get()->getResultArray() as $row) {
            $this->roomTypeId[$row['name']] = (int) $row['id'];
        }

        foreach ($this->db->table('rooms')->select('id, room_number')->get()->getResultArray() as $row) {
            $this->roomId[$row['room_number']] = (int) $row['id'];
        }

        foreach ($this->db->table('hotel_services')->select('id, name, price')->get()->getResultArray() as $row) {
            $this->serviceMeta[$row['name']] = ['id' => (int) $row['id'], 'price' => $row['price']];
        }

        foreach ($this->db->table('users')->select('id, email')->get()->getResultArray() as $row) {
            $this->userId[$row['email']] = (int) $row['id'];
        }

        $this->manager   = $this->userId['manager@ahenfie-demo.test'];
        $this->frontDesk = $this->userId['frontdesk@ahenfie-demo.test'];
        $this->fnb       = $this->userId['fnb@ahenfie-demo.test'];
    }

    private function log(string $module, string $action, int $userId, string $when): void
    {
        $this->db->table('activity_log')->insert([
            'user_id'    => $userId,
            'module'     => $module,
            'action'     => $action,
            'created_at' => $when,
        ]);
    }

    // ---------------------------------------------------------------
    // Staff — a second front-desk agent (night shift) and F&B waiter,
    // so the Staff page isn't just the 3 accounts every earlier phase's
    // testing happened to create.
    // ---------------------------------------------------------------

    private function seedStaff(): void
    {
        $now = date('Y-m-d H:i:s');

        $staff = [
            ['full_name' => 'Kofi Asante', 'email' => 'kofi.asante@ahenfie-demo.test', 'phone' => '+233244112200', 'role' => 'front_desk'],
            ['full_name' => 'Efua Owusu', 'email' => 'efua.owusu@ahenfie-demo.test', 'phone' => '+233244112211', 'role' => 'fnb'],
        ];

        foreach ($staff as $s) {
            $this->db->table('users')->insert([
                'full_name'     => $s['full_name'],
                'email'         => $s['email'],
                'phone'         => $s['phone'],
                'password_hash' => password_hash('Staff123!', PASSWORD_DEFAULT),
                'role'          => $s['role'],
                'is_active'     => 1,
                'created_at'    => $now,
                'updated_at'    => $now,
            ]);
            $this->log('users', 'Created staff user "' . $s['full_name'] . '" (' . $s['role'] . ').', $this->manager, $now);
        }
    }

    // ---------------------------------------------------------------
    // Rooms — Family Suite (room_types id exists from 75d1e3e) had zero
    // actual rooms assigned, so it could never be booked. Two rooms
    // fixes that real gap rather than leaving an unbookable room type.
    // ---------------------------------------------------------------

    private function seedRooms(): void
    {
        $when = '2026-06-15 09:00:00';
        $familyId = $this->roomTypeId['Family Suite'];

        foreach (['401', '402'] as $number) {
            $this->db->table('rooms')->insert([
                'room_number'  => $number,
                'room_type_id' => $familyId,
                'floor'        => '4',
                'status'       => 'clean',
                'created_at'   => $when,
                'updated_at'   => $when,
            ]);
            $this->log('rooms', 'Created room ' . $number . '.', $this->manager, $when);
        }

        foreach (['401', '402'] as $number) {
            $row = $this->db->table('rooms')->select('id')->where('room_number', $number)->get()->getRow();
            $this->roomId[$number] = (int) $row->id;
        }
    }

    // ---------------------------------------------------------------
    // Corporate accounts
    // ---------------------------------------------------------------

    private function seedCorporateAccounts(): void
    {
        $when = '2026-06-18 11:00:00';

        $accounts = [
            ['company_name' => 'MTN Ghana', 'contact_name' => 'Abena Owusu-Sarpong', 'phone' => '+233244112233', 'email' => 'accounts@mtn.com.gh', 'billing_address' => 'MTN House, Independence Avenue, Accra'],
            ['company_name' => 'Ghana Cocoa Board', 'contact_name' => 'Yaw Antwi', 'phone' => '+233302123456', 'email' => 'finance@cocobod.gh', 'billing_address' => 'Cocoa House, Airport City, Accra'],
            ['company_name' => 'Zoomlion Ghana Ltd', 'contact_name' => 'Comfort Mensah', 'phone' => '+233244998877', 'email' => 'travel@zoomlion.com.gh', 'billing_address' => 'Zoomlion House, Accra'],
            ['company_name' => 'Springfield E&P', 'contact_name' => 'Kwesi Ampofo', 'phone' => '+233302556677', 'email' => 'admin@springfieldgroup.com', 'billing_address' => 'Springfield Tower, Airport City, Accra'],
        ];

        foreach ($accounts as $a) {
            $this->db->table('corporate_accounts')->insert(array_merge($a, [
                'created_at' => $when,
                'updated_at' => $when,
            ]));
            $this->log('corporate_accounts', 'Created corporate account "' . $a['company_name'] . '".', $this->manager, $when);
        }

        foreach ($this->db->table('corporate_accounts')->select('id, company_name')->get()->getResultArray() as $row) {
            $this->corporateId[$row['company_name']] = (int) $row['id'];
        }
    }

    // ---------------------------------------------------------------
    // Rate plans + room type mappings
    // ---------------------------------------------------------------

    private function seedRatePlans(): void
    {
        $when = '2026-06-20 10:00:00';

        $plans = [
            ['name' => 'Corporate Negotiated Rate', 'date_start' => '2026-07-01', 'date_end' => '2026-12-31', 'adjustment_type' => 'percentage', 'adjustment_value' => -12.00, 'is_active' => 1, 'types' => ['Standard Room', 'Deluxe Room']],
            ['name' => 'Weekend Getaway', 'date_start' => '2026-08-01', 'date_end' => '2026-10-31', 'adjustment_type' => 'percentage', 'adjustment_value' => -15.00, 'is_active' => 1, 'types' => ['Standard Room', 'Deluxe Room', 'Executive Suite', 'Family Suite']],
            ['name' => 'Long Stay Discount (7+ Nights)', 'date_start' => '2026-01-01', 'date_end' => '2026-12-31', 'adjustment_type' => 'percentage', 'adjustment_value' => -20.00, 'is_active' => 1, 'types' => ['Standard Room', 'Deluxe Room', 'Executive Suite', 'Family Suite']],
            ['name' => 'Festive Season Premium', 'date_start' => '2026-12-15', 'date_end' => '2027-01-05', 'adjustment_type' => 'percentage', 'adjustment_value' => 20.00, 'is_active' => 1, 'types' => ['Executive Suite', 'Family Suite']],
            ['name' => 'Independence Day Special', 'date_start' => '2026-03-01', 'date_end' => '2026-03-08', 'adjustment_type' => 'percentage', 'adjustment_value' => -10.00, 'is_active' => 0, 'types' => ['Standard Room', 'Deluxe Room']],
        ];

        foreach ($plans as $p) {
            $this->db->table('rate_plans')->insert([
                'name'             => $p['name'],
                'date_start'       => $p['date_start'],
                'date_end'         => $p['date_end'],
                'adjustment_type'  => $p['adjustment_type'],
                'adjustment_value' => $p['adjustment_value'],
                'is_active'        => $p['is_active'],
                'created_at'       => $when,
                'updated_at'       => $when,
            ]);
            $planId = (int) $this->db->insertID();

            foreach ($p['types'] as $typeName) {
                $this->db->table('rate_plan_room_types')->insert([
                    'rate_plan_id' => $planId,
                    'room_type_id' => $this->roomTypeId[$typeName],
                ]);
            }

            $this->log('rate_plans', 'Created rate plan "' . $p['name'] . '".', $this->manager, $when);
        }
    }

    // ---------------------------------------------------------------
    // Inventory — deliberately includes several items already at/below
    // reorder_level so the Inventory page's "Low Stock" state has real
    // examples, not just the one pre-existing Bath Towels row.
    // ---------------------------------------------------------------

    private function seedInventory(): void
    {
        $when = '2026-07-01 08:30:00';

        $items = [
            ['name' => 'Bed Sheets (Queen)', 'category' => 'linen', 'quantity_in_stock' => 80, 'unit' => 'sets', 'unit_cost' => 60.00, 'reorder_level' => 20],
            ['name' => 'Pillow Cases', 'category' => 'linen', 'quantity_in_stock' => 150, 'unit' => 'pcs', 'unit_cost' => 15.00, 'reorder_level' => 40],
            ['name' => 'Bath Robes', 'category' => 'linen', 'quantity_in_stock' => 25, 'unit' => 'pcs', 'unit_cost' => 90.00, 'reorder_level' => 10],
            ['name' => 'Shampoo Sachets', 'category' => 'amenities', 'quantity_in_stock' => 300, 'unit' => 'pcs', 'unit_cost' => 3.50, 'reorder_level' => 100],
            ['name' => 'Soap Bars', 'category' => 'amenities', 'quantity_in_stock' => 400, 'unit' => 'pcs', 'unit_cost' => 2.00, 'reorder_level' => 150],
            ['name' => 'Toothpaste Kits', 'category' => 'amenities', 'quantity_in_stock' => 18, 'unit' => 'pcs', 'unit_cost' => 5.00, 'reorder_level' => 50],
            ['name' => 'Coffee Sachets', 'category' => 'amenities', 'quantity_in_stock' => 200, 'unit' => 'pcs', 'unit_cost' => 2.50, 'reorder_level' => 60],
            ['name' => 'Bottled Water 500ml', 'category' => 'fnb', 'quantity_in_stock' => 240, 'unit' => 'pcs', 'unit_cost' => 3.00, 'reorder_level' => 60],
            ['name' => 'Rice (50kg bags)', 'category' => 'fnb', 'quantity_in_stock' => 12, 'unit' => 'bags', 'unit_cost' => 350.00, 'reorder_level' => 5],
            ['name' => 'Cooking Oil (5L)', 'category' => 'fnb', 'quantity_in_stock' => 22, 'unit' => 'litres', 'unit_cost' => 85.00, 'reorder_level' => 8],
            ['name' => 'Chicken (kg)', 'category' => 'fnb', 'quantity_in_stock' => 8, 'unit' => 'kg', 'unit_cost' => 28.00, 'reorder_level' => 15],
            ['name' => 'All-Purpose Cleaner', 'category' => 'cleaning', 'quantity_in_stock' => 45, 'unit' => 'litres', 'unit_cost' => 22.00, 'reorder_level' => 15],
            ['name' => 'Toilet Cleaner', 'category' => 'cleaning', 'quantity_in_stock' => 30, 'unit' => 'litres', 'unit_cost' => 18.00, 'reorder_level' => 10],
            ['name' => 'Mop Heads', 'category' => 'cleaning', 'quantity_in_stock' => 14, 'unit' => 'pcs', 'unit_cost' => 20.00, 'reorder_level' => 15],
            ['name' => 'Trash Bags', 'category' => 'cleaning', 'quantity_in_stock' => 60, 'unit' => 'rolls', 'unit_cost' => 12.00, 'reorder_level' => 20],
            ['name' => 'Light Bulbs (LED)', 'category' => 'other', 'quantity_in_stock' => 5, 'unit' => 'pcs', 'unit_cost' => 15.00, 'reorder_level' => 20],
            ['name' => 'Printer Paper', 'category' => 'other', 'quantity_in_stock' => 8, 'unit' => 'reams', 'unit_cost' => 30.00, 'reorder_level' => 10],
            ['name' => 'Guest Room Key Cards', 'category' => 'other', 'quantity_in_stock' => 200, 'unit' => 'pcs', 'unit_cost' => 4.00, 'reorder_level' => 50],
        ];

        foreach ($items as $item) {
            $this->db->table('inventory_items')->insert(array_merge($item, [
                'created_at' => $when,
                'updated_at' => $when,
            ]));
            $this->log('inventory', 'Added inventory item "' . $item['name'] . '".', $this->manager, $when);
        }
    }

    // ---------------------------------------------------------------
    // Dining areas & tables
    // ---------------------------------------------------------------

    private function seedDining(): void
    {
        $when = '2026-06-22 09:00:00';

        $areas = [
            'Indoor Restaurant' => 'Air-conditioned main dining hall, adjoining the lobby',
            'Poolside Bar'      => 'Casual seating and light bites by the pool',
        ];

        $areaId = [];
        foreach ($areas as $name => $description) {
            $this->db->table('dining_areas')->insert([
                'name'        => $name,
                'description' => $description,
                'created_at'  => $when,
                'updated_at'  => $when,
            ]);
            $areaId[$name] = (int) $this->db->insertID();
            $this->log('dining', 'Created dining area "' . $name . '".', $this->fnb, $when);
        }

        $tables = [
            'T2' => ['area' => 'Indoor Restaurant', 'capacity' => 4],
            'T3' => ['area' => 'Indoor Restaurant', 'capacity' => 4],
            'T4' => ['area' => 'Indoor Restaurant', 'capacity' => 6],
            'T5' => ['area' => 'Indoor Restaurant', 'capacity' => 2],
            'T6' => ['area' => 'Indoor Restaurant', 'capacity' => 8],
            'T7' => ['area' => 'Poolside Bar', 'capacity' => 4],
            'T8' => ['area' => 'Poolside Bar', 'capacity' => 4],
            'T9' => ['area' => 'Poolside Bar', 'capacity' => 2],
        ];

        foreach ($tables as $number => $t) {
            $this->db->table('dining_tables')->insert([
                'table_number'     => $number,
                'dining_area_id'   => $areaId[$t['area']],
                'seating_capacity' => $t['capacity'],
                'created_at'       => $when,
                'updated_at'       => $when,
            ]);
            $this->log('dining', 'Created table ' . $number . '.', $this->fnb, $when);
        }

        foreach ($this->db->table('dining_tables')->select('id, table_number')->get()->getResultArray() as $row) {
            $this->tableId[$row['table_number']] = (int) $row['id'];
        }
    }

    // ---------------------------------------------------------------
    // Menu items
    // ---------------------------------------------------------------

    private function seedMenu(): void
    {
        $when = '2026-06-23 09:30:00';

        $items = [
            ['name' => 'Spring Rolls', 'price' => 35.00, 'category' => 'Starters'],
            ['name' => 'Chicken Wings', 'price' => 40.00, 'category' => 'Starters'],
            ['name' => 'Spinach & Feta Salad', 'price' => 30.00, 'category' => 'Starters'],
            ['name' => 'Pepper Soup', 'price' => 38.00, 'category' => 'Starters'],
            ['name' => 'Banku & Tilapia', 'price' => 65.00, 'category' => 'Mains'],
            ['name' => 'Waakye Special', 'price' => 55.00, 'category' => 'Mains'],
            ['name' => 'Fufu & Light Soup', 'price' => 60.00, 'category' => 'Mains'],
            ['name' => 'Grilled Chicken', 'price' => 70.00, 'category' => 'Mains'],
            ['name' => 'Beef Kebab', 'price' => 58.00, 'category' => 'Mains'],
            ['name' => 'Vegetable Fried Rice', 'price' => 48.00, 'category' => 'Mains'],
            ['name' => 'Red Red', 'price' => 42.00, 'category' => 'Mains'],
            ['name' => 'Fried Plantain', 'price' => 20.00, 'category' => 'Sides'],
            ['name' => 'Coleslaw', 'price' => 15.00, 'category' => 'Sides'],
            ['name' => 'French Fries', 'price' => 25.00, 'category' => 'Sides'],
            ['name' => 'Star Beer', 'price' => 18.00, 'category' => 'Drinks'],
            ['name' => 'Sobolo', 'price' => 12.00, 'category' => 'Drinks'],
            ['name' => 'Bottled Water', 'price' => 8.00, 'category' => 'Drinks'],
            ['name' => 'Fresh Pineapple Juice', 'price' => 20.00, 'category' => 'Drinks'],
            ['name' => 'Coca-Cola', 'price' => 12.00, 'category' => 'Drinks'],
            ['name' => 'Fruit Platter', 'price' => 25.00, 'category' => 'Desserts'],
            ['name' => 'Chocolate Cake', 'price' => 30.00, 'category' => 'Desserts'],
            ['name' => 'Ice Cream', 'price' => 20.00, 'category' => 'Desserts'],
        ];

        foreach ($items as $item) {
            $this->db->table('menu_items')->insert(array_merge($item, [
                'is_available' => 1,
                'created_at'   => $when,
                'updated_at'   => $when,
            ]));
            $this->log('pos', 'Added menu item "' . $item['name'] . '".', $this->fnb, $when);
        }

        foreach ($this->db->table('menu_items')->select('id, name, price')->get()->getResultArray() as $row) {
            $this->menuItemMeta[$row['name']] = ['id' => (int) $row['id'], 'price' => $row['price']];
        }
    }

    // ---------------------------------------------------------------
    // Guests + bookings + folios — the core of "looks like a hotel
    // that's actually been operating." Spans 2026-06-20 through an
    // upcoming 2026-09-14, covering every booking status, corporate
    // billing, a repeat guest, a blacklisted guest with the refund that
    // explains why, and a currently in-house guest.
    // ---------------------------------------------------------------

    private function seedGuestsAndBookings(): void
    {
        $guests = [
            'Kofi Owusu'            => ['phone' => '+233244556677', 'email' => 'kofi.owusu@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-101202233-1', 'nationality' => 'Ghanaian', 'segment' => 'business'],
            'Efua Asiedu'           => ['phone' => '+233201234567', 'email' => 'efua.asiedu@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-100455221-3', 'nationality' => 'Ghanaian', 'segment' => 'leisure'],
            'Yaw Boateng'           => ['phone' => '+233267891234', 'email' => null, 'id_type' => 'ghana_card', 'id_number' => 'GHA-100998877-2', 'nationality' => 'Ghanaian', 'segment' => 'leisure'],
            'Adwoa Nyarko'          => ['phone' => '+233551239876', 'email' => 'adwoa.nyarko@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-100223344-5', 'nationality' => 'Ghanaian', 'segment' => 'vip'],
            'Kwabena Owusu-Ansah'   => ['phone' => '+233209876543', 'email' => 'kwabena.oa@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-100776655-8', 'nationality' => 'Ghanaian', 'segment' => 'business'],
            'Akosua Frimpong'       => ['phone' => '+233244001122', 'email' => 'akosua.frimpong@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-100334455-6', 'nationality' => 'Ghanaian', 'segment' => 'leisure'],
            'Nana Kwame Asare'      => ['phone' => '+233277654321', 'email' => 'nana.asare@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-100887766-1', 'nationality' => 'Ghanaian', 'segment' => 'vip'],
            'Ama Serwaa'            => ['phone' => '+233551002233', 'email' => null, 'id_type' => 'ghana_card', 'id_number' => 'GHA-100665544-9', 'nationality' => 'Ghanaian', 'segment' => 'leisure'],
            'Emmanuel Tetteh'       => ['phone' => '+233244887766', 'email' => 'emmanuel.tetteh@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-100112233-4', 'nationality' => 'Ghanaian', 'segment' => 'business'],
            'Comfort Adjei'         => ['phone' => '+233201998877', 'email' => 'comfort.adjei@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-100554433-7', 'nationality' => 'Ghanaian', 'segment' => 'leisure'],
            'David Chen'            => ['phone' => '+233559112233', 'email' => 'david.chen@example.com', 'id_type' => 'passport', 'id_number' => 'P8891122', 'nationality' => 'American', 'segment' => 'business'],
            'Sarah Johnson'         => ['phone' => '+233559998877', 'email' => 'sarah.johnson@example.com', 'id_type' => 'passport', 'id_number' => 'P7765544', 'nationality' => 'British', 'segment' => 'leisure'],
            'Ibrahim Mahama'        => ['phone' => '+233244321654', 'email' => 'ibrahim.mahama@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-100998811-3', 'nationality' => 'Ghanaian', 'segment' => 'business'],
            'Linda Owusu'           => ['phone' => '+233277123456', 'email' => null, 'id_type' => 'ghana_card', 'id_number' => 'GHA-100221100-2', 'nationality' => 'Ghanaian', 'segment' => 'leisure'],
            'Michael Osei'          => ['phone' => '+233201765432', 'email' => 'michael.osei@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-100443322-9', 'nationality' => 'Ghanaian', 'segment' => 'vip'],
            'Grace Adutwum'         => ['phone' => '+233244998811', 'email' => 'grace.adutwum@example.com', 'id_type' => 'ghana_card', 'id_number' => 'GHA-100112244-6', 'nationality' => 'Ghanaian', 'segment' => 'business',
                'is_blacklisted' => 1, 'notes' => 'Disputed a folio charge and reversed a card payment after her July 2026 stay — front desk to require prepayment for any future booking.'],
        ];

        // guest => room number, checkin, checkout, status, source, rate/night,
        // [payment: method/amount], [services: [name, qty]], [corporate: company], [refund: amount/reason]
        $bookings = [
            ['guest' => 'Grace Adutwum', 'room' => '401', 'checkin' => '2026-07-05', 'checkout' => '2026-07-07', 'status' => 'checked_out', 'source' => 'walk_in', 'rate' => '1500.00', 'payment' => ['method' => 'card', 'amount' => '3000.00'], 'refund' => ['amount' => '300.00', 'reason' => 'Guest disputed a folio charge; partial refund issued']],
            ['guest' => 'Kofi Owusu', 'room' => '103', 'checkin' => '2026-06-20', 'checkout' => '2026-06-23', 'status' => 'checked_out', 'source' => 'walk_in', 'rate' => '450.00', 'payment' => ['method' => 'cash', 'amount' => '1350.00']],
            ['guest' => 'Efua Asiedu', 'room' => '201', 'checkin' => '2026-06-25', 'checkout' => '2026-06-27', 'status' => 'checked_out', 'source' => 'phone', 'rate' => '750.00', 'payment' => ['method' => 'mobile_money', 'amount' => '1500.00']],
            ['guest' => 'Yaw Boateng', 'room' => '301', 'checkin' => '2026-07-02', 'checkout' => '2026-07-05', 'status' => 'checked_out', 'source' => 'walk_in', 'rate' => '1200.00', 'payment' => ['method' => 'card', 'amount' => '3840.00'], 'services' => [['name' => 'Breakfast', 'qty' => 3]]],
            ['guest' => 'Adwoa Nyarko', 'room' => '104', 'checkin' => '2026-07-10', 'checkout' => '2026-07-12', 'status' => 'checked_out', 'source' => 'corporate', 'rate' => '450.00', 'payment' => ['method' => 'bank_transfer', 'amount' => '900.00'], 'corporate' => 'MTN Ghana'],
            ['guest' => 'Kwabena Owusu-Ansah', 'room' => '203', 'checkin' => '2026-07-15', 'checkout' => '2026-07-18', 'status' => 'checked_out', 'source' => 'corporate', 'rate' => '660.00', 'payment' => ['method' => 'bank_transfer', 'amount' => '1980.00'], 'corporate' => 'Ghana Cocoa Board'],
            // Full rate, not the Weekend Getaway discount - that plan only
            // starts 2026-08-01, and this stay is in late July.
            ['guest' => 'Nana Kwame Asare', 'room' => '302', 'checkin' => '2026-07-25', 'checkout' => '2026-07-28', 'status' => 'checked_out', 'source' => 'online', 'rate' => '1200.00', 'payment' => ['method' => 'card', 'amount' => '3600.00']],
            ['guest' => 'Akosua Frimpong', 'room' => '401', 'checkin' => '2026-07-30', 'checkout' => '2026-08-01', 'status' => 'checked_out', 'source' => 'walk_in', 'rate' => '1500.00', 'payment' => ['method' => 'mobile_money', 'amount' => '3000.00']],
            ['guest' => 'Ama Serwaa', 'room' => '103', 'checkin' => '2026-07-28', 'checkout' => '2026-07-30', 'status' => 'checked_out', 'source' => 'walk_in', 'rate' => '450.00', 'payment' => ['method' => 'cash', 'amount' => '900.00']],
            ['guest' => 'Emmanuel Tetteh', 'room' => '201', 'checkin' => '2026-08-01', 'checkout' => '2026-08-03', 'status' => 'checked_out', 'source' => 'phone', 'rate' => '750.00', 'payment' => ['method' => 'mobile_money', 'amount' => '1650.00'], 'services' => [['name' => 'Airport Pickup', 'qty' => 1]]],
            ['guest' => 'Comfort Adjei', 'room' => '104', 'checkin' => '2026-08-04', 'checkout' => '2026-08-06', 'status' => 'checked_out', 'source' => 'online', 'rate' => '450.00', 'payment' => ['method' => 'card', 'amount' => '900.00']],
            ['guest' => 'David Chen', 'room' => '301', 'checkin' => '2026-08-07', 'checkout' => '2026-08-09', 'status' => 'checked_out', 'source' => 'walk_in', 'rate' => '1200.00', 'payment' => ['method' => 'card', 'amount' => '2400.00']],
            ['guest' => 'Sarah Johnson', 'room' => '203', 'checkin' => '2026-08-08', 'checkout' => '2026-08-10', 'status' => 'checked_out', 'source' => 'online', 'rate' => '750.00', 'payment' => ['method' => 'mobile_money', 'amount' => '1500.00']],
            ['guest' => 'Ibrahim Mahama', 'room' => '101', 'checkin' => '2026-07-05', 'checkout' => '2026-07-07', 'status' => 'cancelled', 'source' => 'phone', 'rate' => '450.00'],
            ['guest' => 'Linda Owusu', 'room' => '102', 'checkin' => '2026-07-12', 'checkout' => '2026-07-13', 'status' => 'no_show', 'source' => 'walk_in', 'rate' => '450.00'],
            // Room 999 is a Standard Room (base 450) despite the odd number -
            // no rate plan applies here, so the rate matches the room type.
            ['guest' => 'Michael Osei', 'room' => '999', 'checkin' => '2026-08-11', 'checkout' => '2026-08-14', 'status' => 'checked_in', 'source' => 'walk_in', 'rate' => '450.00', 'payment' => ['method' => 'mobile_money', 'amount' => '900.00']],
            ['guest' => 'Akosua Frimpong', 'room' => '101', 'checkin' => '2026-08-15', 'checkout' => '2026-08-17', 'status' => 'confirmed', 'source' => 'online', 'rate' => '450.00'],
            ['guest' => 'Ibrahim Mahama', 'room' => '104', 'checkin' => '2026-08-18', 'checkout' => '2026-08-20', 'status' => 'pending_deposit', 'source' => 'online', 'rate' => '450.00'],
            ['guest' => 'Linda Owusu', 'room' => '201', 'checkin' => '2026-08-20', 'checkout' => '2026-08-23', 'status' => 'confirmed', 'source' => 'phone', 'rate' => '750.00'],
            ['guest' => 'David Chen', 'room' => '203', 'checkin' => '2026-08-25', 'checkout' => '2026-08-28', 'status' => 'confirmed', 'source' => 'corporate', 'rate' => '660.00'],
            ['guest' => 'Comfort Adjei', 'room' => '103', 'checkin' => '2026-09-05', 'checkout' => '2026-09-07', 'status' => 'confirmed', 'source' => 'online', 'rate' => '450.00', 'adults' => 2, 'children' => 1, 'services' => [['name' => 'Extra Bed', 'qty' => 1]]],
            ['guest' => 'Sarah Johnson', 'room' => '402', 'checkin' => '2026-09-10', 'checkout' => '2026-09-14', 'status' => 'pending_deposit', 'source' => 'online', 'rate' => '1200.00'],
        ];

        // --- Insert guests, dated to their earliest booking (front desk
        // creates the guest record at first-booking time in the real app —
        // see BookingWizardController::saveGuest()). ---

        $firstSeen = [];
        foreach ($bookings as $b) {
            $when = $this->bookingCreatedAt($b['checkin'], $b['source']);
            if (! isset($firstSeen[$b['guest']]) || $when < $firstSeen[$b['guest']]) {
                $firstSeen[$b['guest']] = $when;
            }
        }

        foreach ($guests as $name => $g) {
            $when = $firstSeen[$name];
            $this->db->table('guests')->insert([
                'full_name'      => $name,
                'phone'          => $g['phone'],
                'email'          => $g['email'],
                'id_type'        => $g['id_type'],
                'id_number'      => $g['id_number'],
                'nationality'    => $g['nationality'],
                'segment'        => $g['segment'],
                'is_blacklisted' => $g['is_blacklisted'] ?? 0,
                'notes'          => $g['notes'] ?? null,
                'created_at'     => $when,
                'updated_at'     => $when,
            ]);
            $this->guestId[$name] = (int) $this->db->insertID();
            $this->log('guest', 'Created guest "' . $name . '" during booking.', $this->frontDesk, $when);
        }

        // --- Insert bookings + matching folio/line-items/payments. ---

        foreach ($bookings as $b) {
            $when    = $this->bookingCreatedAt($b['checkin'], $b['source']);
            $guestId = $this->guestId[$b['guest']];
            $roomId  = $this->roomId[$b['room']];

            $this->db->table('bookings')->insert([
                'guest_id'       => $guestId,
                'room_id'        => $roomId,
                'check_in_date'  => $b['checkin'],
                'check_out_date' => $b['checkout'],
                'status'         => $b['status'],
                'source'         => $b['source'],
                'rate_applied'   => $b['rate'],
                'adults'         => $b['adults'] ?? 1,
                'children'       => $b['children'] ?? 0,
                'created_at'     => $when,
                'updated_at'     => $when,
            ]);
            $bookingId = (int) $this->db->insertID();

            $this->log('bookings', 'Booked room ' . $b['room'] . ' for ' . $b['checkin'] . ' to ' . $b['checkout'] . '.', $this->frontDesk, $when);

            if (! empty($b['services'])) {
                foreach ($b['services'] as $svc) {
                    $meta = $this->serviceMeta[$svc['name']];
                    $this->db->table('booking_service_items')->insert([
                        'booking_id'   => $bookingId,
                        'service_id'   => $meta['id'],
                        'service_name' => $svc['name'],
                        'unit_price'   => $meta['price'],
                        'quantity'     => $svc['qty'],
                        'created_at'   => $when,
                    ]);
                }
            }

            if (! in_array($b['status'], ['checked_out', 'checked_in'], true)) {
                continue; // no folio until check-in, matching CheckInController
            }

            $checkinAt  = $b['checkin'] . ' 14:00:00';
            $isClosed   = $b['status'] === 'checked_out';
            $checkoutAt = $isClosed ? $b['checkout'] . ' 11:00:00' : null;

            $this->db->table('folios')->insert([
                'booking_id'           => $bookingId,
                'guest_id'             => $guestId,
                'corporate_account_id' => isset($b['corporate']) ? $this->corporateId[$b['corporate']] : null,
                'status'               => $isClosed ? 'closed' : 'open',
                'opened_at'            => $checkinAt,
                'closed_at'            => $checkoutAt,
                'created_at'           => $checkinAt,
                'updated_at'           => $checkoutAt ?? $checkinAt,
            ]);
            $folioId = (int) $this->db->insertID();

            $this->log('checkin', 'Checked in booking #' . $bookingId . '.', $this->frontDesk, $checkinAt);

            // Nightly room charges — every night of the stay is posted at
            // check-in time, matching CheckInController::postNightlyCharges().
            $date = $b['checkin'];
            while ($date < $b['checkout']) {
                $this->db->table('folio_line_items')->insert([
                    'folio_id'    => $folioId,
                    'description' => 'Room charge — ' . $date,
                    'amount'      => $b['rate'],
                    'category'    => 'room_charge',
                    'item_date'   => $date,
                    'created_by'  => $this->frontDesk,
                    'created_at'  => $checkinAt,
                    'updated_at'  => $checkinAt,
                ]);
                $date = date('Y-m-d', strtotime($date . ' +1 day'));
            }

            if (! empty($b['services'])) {
                foreach ($b['services'] as $svc) {
                    $meta   = $this->serviceMeta[$svc['name']];
                    $amount = money_multiply($meta['price'], $svc['qty']);
                    $this->db->table('folio_line_items')->insert([
                        'folio_id'    => $folioId,
                        'description' => $svc['name'] . ($svc['qty'] > 1 ? ' x' . $svc['qty'] : ''),
                        'amount'      => $amount,
                        'category'    => 'other',
                        'item_date'   => $b['checkin'],
                        'created_by'  => $this->frontDesk,
                        'created_at'  => $checkinAt,
                        'updated_at'  => $checkinAt,
                    ]);
                }
            }

            if (! empty($b['payment'])) {
                $paidAt = $isClosed ? $b['checkout'] . ' 10:30:00' : $checkinAt;
                $this->db->table('folio_payments')->insert([
                    'folio_id'    => $folioId,
                    'amount'      => $b['payment']['amount'],
                    'is_refund'   => 0,
                    'method'      => $b['payment']['method'],
                    'reference'   => null,
                    'recorded_by' => $this->frontDesk,
                    'paid_at'     => $paidAt,
                    'created_at'  => $paidAt,
                ]);
                $this->log('folio', 'Recorded ' . $b['payment']['method'] . ' payment of ' . format_money($b['payment']['amount']) . ' on folio #' . $folioId . '.', $this->frontDesk, $paidAt);
            }

            if (! empty($b['refund'])) {
                $refundAt = date('Y-m-d H:i:s', strtotime($b['checkout'] . ' +3 days'));
                $this->db->table('folio_payments')->insert([
                    'folio_id'      => $folioId,
                    'amount'        => $b['refund']['amount'],
                    'is_refund'     => 1,
                    'refund_reason' => $b['refund']['reason'],
                    'method'        => 'card',
                    'reference'     => null,
                    'recorded_by'   => $this->manager,
                    'paid_at'       => $refundAt,
                    'created_at'    => $refundAt,
                ]);
                $this->log('folio', 'Refunded ' . format_money($b['refund']['amount']) . ' on folio #' . $folioId . ' (' . $b['refund']['reason'] . ').', $this->manager, $refundAt);
            }

            if ($isClosed) {
                $this->log('checkout', 'Checked out booking #' . $bookingId . '.', $this->frontDesk, $checkoutAt);
            }
        }
    }

    private function bookingCreatedAt(string $checkin, string $source): string
    {
        if ($source === 'walk_in') {
            return $checkin . ' 14:00:00';
        }

        return date('Y-m-d H:i:s', strtotime($checkin . ' -7 days 10:15:00'));
    }

    // ---------------------------------------------------------------
    // Event clients & events — after guests, since 3 of the 5 events
    // below reference guest_id.
    // ---------------------------------------------------------------

    private function seedEventClientsAndEvents(): void
    {
        $when = '2026-07-05 10:00:00';

        $clients = [
            ['company_name' => 'Golden Star Resources', 'contact_name' => 'Efua Danquah', 'phone' => '+233244665544', 'email' => 'events@goldenstar.com.gh', 'catering_preferences' => 'Halal options required for roughly 30% of guests; no alcohol during morning sessions.'],
            ['company_name' => 'Trust Insurance Ghana', 'contact_name' => 'Kwame Adom', 'phone' => '+233302887766', 'email' => 'corporate@trustinsurance.gh', 'catering_preferences' => null],
        ];

        $clientId = [];
        foreach ($clients as $c) {
            $this->db->table('event_clients')->insert(array_merge($c, [
                'created_at' => $when,
                'updated_at' => $when,
            ]));
            $clientId[$c['company_name']] = (int) $this->db->insertID();
            $this->log('events', 'Created event client "' . $c['company_name'] . '".', $this->fnb, $when);
        }

        $events = [
            ['event_name' => 'Golden Star Resources Quarterly Review', 'event_type' => 'Conference', 'client' => 'Golden Star Resources', 'event_date' => '2026-07-18', 'expected_headcount' => 60, 'venue_space' => 'Grand Ballroom', 'status' => 'completed', 'contract_notes' => 'Full-day session with lunch and two coffee breaks.'],
            ['event_name' => 'Trust Insurance Team Building', 'event_type' => 'Corporate', 'client' => 'Trust Insurance Ghana', 'event_date' => '2026-09-05', 'expected_headcount' => 35, 'venue_space' => 'Indoor Restaurant', 'status' => 'confirmed', 'contract_notes' => 'Outdoor games in the afternoon, dinner to follow.'],
            ['event_name' => 'Osei-Mensah Wedding', 'event_type' => 'Wedding', 'guest' => 'Nana Kwame Asare', 'event_date' => '2026-08-22', 'expected_headcount' => 150, 'venue_space' => 'Poolside Lawn', 'status' => 'confirmed', 'contract_notes' => '50% deposit received; final headcount due one week prior.'],
            ['event_name' => 'Adutwum Birthday Celebration', 'event_type' => 'Birthday', 'guest' => 'Grace Adutwum', 'event_date' => '2026-06-14', 'expected_headcount' => 40, 'venue_space' => 'Main Terrace', 'status' => 'completed', 'contract_notes' => null],
            ['event_name' => 'Frimpong Naming Ceremony', 'event_type' => 'Naming Ceremony', 'guest' => 'Akosua Frimpong', 'event_date' => '2026-08-30', 'expected_headcount' => 25, 'venue_space' => 'Main Terrace', 'status' => 'inquiry', 'contract_notes' => null],
        ];

        foreach ($events as $e) {
            $eventWhen = date('Y-m-d H:i:s', strtotime($e['event_date'] . ' -20 days'));

            $this->db->table('events')->insert([
                'event_name'         => $e['event_name'],
                'event_type'         => $e['event_type'],
                'guest_id'           => isset($e['guest']) ? $this->guestId[$e['guest']] : null,
                'event_client_id'    => isset($e['client']) ? $clientId[$e['client']] : null,
                'event_date'         => $e['event_date'],
                'expected_headcount' => $e['expected_headcount'],
                'venue_space'        => $e['venue_space'],
                'status'             => $e['status'],
                'contract_notes'     => $e['contract_notes'],
                'created_at'         => $eventWhen,
                'updated_at'         => $eventWhen,
            ]);
            $this->log('events', 'Created event "' . $e['event_name'] . '" for ' . $e['event_date'] . '.', $this->fnb, $eventWhen);
        }
    }

    // ---------------------------------------------------------------
    // Housekeeping — daily turnover history for the past few weeks,
    // plus two tasks in flight today that explain the two rooms
    // currently showing dirty/occupied on the Room Management board.
    // ---------------------------------------------------------------

    private function seedHousekeeping(): void
    {
        $taskTypes  = ['standard_cleaning', 'deep_cleaning', 'turn_down', 'vacate_cleaning'];
        $priorities = ['low', 'medium', 'high'];
        $rooms      = ['101', '103', '104', '201', '203', '301', '302', '401', '402', '999'];

        $day = strtotime('2026-07-22');
        $end = strtotime('2026-08-11');
        $i   = 0;

        while ($day <= $end) {
            $date        = date('Y-m-d', $day);
            $room        = $rooms[$i % count($rooms)];
            $type        = $taskTypes[$i % count($taskTypes)];
            $priority    = $priorities[$i % count($priorities)];
            $start       = $date . ' 10:00:00';
            $completed   = $date . ' 10:35:00';
            $inspect     = $i % 2 === 0;
            $inspectedAt = date('Y-m-d H:i:s', strtotime($completed . ' +25 minutes'));

            $this->db->table('housekeeping_tasks')->insert([
                'room_id'      => $this->roomId[$room],
                'task_type'    => $type,
                'priority'     => $priority,
                'status'       => 'completed',
                'assigned_to'  => $this->frontDesk,
                'start_time'   => $start,
                'completed_at' => $completed,
                'inspected_at' => $inspect ? $inspectedAt : null,
                'inspected_by' => $inspect ? $this->manager : null,
                'notes'        => null,
                'created_at'   => $start,
                'updated_at'   => $inspect ? $inspectedAt : $completed,
            ]);
            $taskId = (int) $this->db->insertID();

            $this->log('housekeeping', 'Created ' . $type . ' task for room ' . $room . '.', $this->frontDesk, $start);
            $this->log('housekeeping', 'Completed task #' . $taskId . ' for room ' . $room . '.', $this->frontDesk, $completed);
            if ($inspect) {
                $this->log('housekeeping', 'Inspected task #' . $taskId . '.', $this->manager, $inspectedAt);
            }

            $day += 86400;
            $i++;
        }

        // Today: room 102 (shows "dirty" on the board) is mid-clean, and
        // room 202's current guest (booking #10, checked in today) has a
        // turn-down scheduled for this evening.
        $this->db->table('housekeeping_tasks')->insert([
            'room_id'     => $this->roomId['102'],
            'task_type'   => 'vacate_cleaning',
            'priority'    => 'medium',
            'status'      => 'in_progress',
            'assigned_to' => $this->frontDesk,
            'start_time'  => '2026-08-12 09:15:00',
            'created_at'  => '2026-08-12 09:15:00',
            'updated_at'  => '2026-08-12 09:15:00',
        ]);
        $vacateId = (int) $this->db->insertID();
        $this->log('housekeeping', 'Created vacate_cleaning task for room 102.', $this->frontDesk, '2026-08-12 09:15:00');
        $this->log('housekeeping', 'Started task #' . $vacateId . '.', $this->frontDesk, '2026-08-12 09:16:00');

        $this->db->table('housekeeping_tasks')->insert([
            'room_id'    => $this->roomId['202'],
            'task_type'  => 'turn_down',
            'priority'   => 'low',
            'status'     => 'not_started',
            'created_at' => '2026-08-12 09:30:00',
            'updated_at' => '2026-08-12 09:30:00',
        ]);
        $this->log('housekeeping', 'Created turn_down task for room 202.', $this->frontDesk, '2026-08-12 09:30:00');
    }

    // ---------------------------------------------------------------
    // Maintenance
    // ---------------------------------------------------------------

    private function seedMaintenance(): void
    {
        $orders = [
            ['title' => 'Leaking Faucet - Room 103', 'asset' => 'Room 103 Bathroom Faucet', 'room' => '103', 'priority' => 'medium', 'status' => 'completed', 'reported' => '2026-07-18', 'due' => '2026-07-20', 'completed' => '2026-07-19', 'notes' => 'Replaced washer, tested — no more drips.'],
            ['title' => 'TV Remote Not Working - Room 201', 'asset' => 'Room 201 TV Remote', 'room' => '201', 'priority' => 'low', 'status' => 'completed', 'reported' => '2026-07-23', 'due' => '2026-07-25', 'completed' => '2026-07-24', 'notes' => 'Replaced batteries, confirmed working.'],
            ['title' => 'Broken Window Latch - Room 301', 'asset' => 'Room 301 Window Latch', 'room' => '301', 'priority' => 'high', 'status' => 'open', 'reported' => '2026-08-03', 'due' => '2026-08-05', 'notes' => "Guest reported latch won't lock; parts on order."],
            ['title' => 'Pool Pump Servicing', 'asset' => 'Pool Filtration Pump', 'room' => null, 'priority' => 'medium', 'status' => 'in_progress', 'reported' => '2026-08-09', 'due' => '2026-08-15'],
            ['title' => 'Generator Routine Check', 'asset' => 'Backup Generator', 'room' => null, 'priority' => 'low', 'status' => 'completed', 'reported' => '2026-07-28', 'due' => '2026-07-30', 'completed' => '2026-07-29', 'notes' => 'Monthly service completed, oil changed.'],
            ['title' => 'WiFi Router Replacement - Lobby', 'asset' => 'Lobby Router', 'room' => null, 'priority' => 'medium', 'status' => 'completed', 'reported' => '2026-08-01', 'due' => '2026-08-02', 'completed' => '2026-08-02', 'notes' => 'Old unit was dropping connections; swapped for a new router.'],
            ['title' => 'Clogged Drain - Room 104', 'asset' => 'Room 104 Bathroom Drain', 'room' => '104', 'priority' => 'medium', 'status' => 'open', 'reported' => '2026-08-11', 'due' => '2026-08-14'],
        ];

        foreach ($orders as $o) {
            $reportedAt = $o['reported'] . ' 09:00:00';

            $this->db->table('maintenance_work_orders')->insert([
                'title'        => $o['title'],
                'asset'        => $o['asset'],
                'room_id'      => $o['room'] ? $this->roomId[$o['room']] : null,
                'priority'     => $o['priority'],
                'status'       => $o['status'],
                'reported_by'  => $this->frontDesk,
                'assigned_to'  => $o['status'] === 'open' ? null : $this->manager,
                'due_date'     => $o['due'],
                'completed_at' => isset($o['completed']) ? $o['completed'] . ' 15:00:00' : null,
                'notes'        => $o['notes'] ?? null,
                'created_at'   => $reportedAt,
                'updated_at'   => isset($o['completed']) ? $o['completed'] . ' 15:00:00' : $reportedAt,
            ]);
            $id = (int) $this->db->insertID();

            $this->log('maintenance', 'Created work order "' . $o['title'] . '".', $this->frontDesk, $reportedAt);
            if ($o['status'] === 'in_progress') {
                $this->log('maintenance', 'Started work order #' . $id . '.', $this->manager, date('Y-m-d H:i:s', strtotime($reportedAt . ' +1 day')));
            }
            if (isset($o['completed'])) {
                $this->log('maintenance', 'Started work order #' . $id . '.', $this->manager, date('Y-m-d H:i:s', strtotime($reportedAt . ' +2 hours')));
                $this->log('maintenance', 'Completed work order #' . $id . '.', $this->manager, $o['completed'] . ' 15:00:00');
            }
        }
    }

    // ---------------------------------------------------------------
    // Restaurant reservations
    // ---------------------------------------------------------------

    private function seedRestaurantReservations(): void
    {
        $reservations = [
            ['guest' => 'Kofi Owusu', 'party' => 4, 'date' => '2026-07-28', 'time' => '19:30:00', 'table' => 'T3', 'status' => 'completed'],
            ['walkIn' => ['Nii Armah', '+233244001199'], 'party' => 2, 'date' => '2026-07-30', 'time' => '12:30:00', 'table' => 'T7', 'status' => 'completed'],
            ['guest' => 'Adwoa Nyarko', 'party' => 2, 'date' => '2026-08-02', 'time' => '20:00:00', 'table' => 'T5', 'status' => 'completed'],
            ['walkIn' => ['Yaa Asantewaa', '+233201122334'], 'party' => 6, 'date' => '2026-08-03', 'time' => '19:00:00', 'table' => 'T4', 'status' => 'no_show'],
            ['guest' => 'Emmanuel Tetteh', 'party' => 2, 'date' => '2026-08-05', 'time' => '13:00:00', 'table' => 'T9', 'status' => 'completed'],
            ['guest' => 'Nana Kwame Asare', 'party' => 8, 'date' => '2026-08-06', 'time' => '19:30:00', 'table' => 'T6', 'status' => 'completed'],
            ['walkIn' => ['Kojo Antwi', '+233209988776'], 'party' => 4, 'date' => '2026-08-09', 'time' => '18:30:00', 'table' => 'T2', 'status' => 'cancelled'],
            ['guest' => 'Sarah Johnson', 'party' => 2, 'date' => '2026-08-10', 'time' => '12:00:00', 'table' => 'T8', 'status' => 'completed'],
            ['guest' => 'Michael Osei', 'party' => 2, 'date' => '2026-08-12', 'time' => '19:30:00', 'table' => 'T3', 'status' => 'seated'],
            ['walkIn' => ['Abena Owusu', '+233244556611'], 'party' => 4, 'date' => '2026-08-12', 'time' => '20:00:00', 'table' => 'T4', 'status' => 'booked'],
            ['walkIn' => ['Efo Mensah', '+233277112233'], 'party' => 3, 'date' => '2026-08-14', 'time' => '19:00:00', 'table' => 'T6', 'status' => 'booked'],
            ['guest' => 'Ibrahim Mahama', 'party' => 2, 'date' => '2026-08-16', 'time' => '18:00:00', 'table' => 'T9', 'status' => 'booked'],
        ];

        foreach ($reservations as $r) {
            $when = $r['date'] . ' ' . date('H:i:s', strtotime($r['time']) - 3600 * 6);
            $name = $r['guest'] ?? $r['walkIn'][0];

            $this->db->table('restaurant_reservations')->insert([
                'guest_id'         => isset($r['guest']) ? $this->guestId[$r['guest']] : null,
                'walk_in_name'     => $r['walkIn'][0] ?? null,
                'walk_in_phone'    => $r['walkIn'][1] ?? null,
                'party_size'       => $r['party'],
                'reservation_date' => $r['date'],
                'reservation_time' => $r['time'],
                'dining_table_id'  => $this->tableId[$r['table']],
                'status'           => $r['status'],
                'created_at'       => $when,
                'updated_at'       => $when,
            ]);
            $id = (int) $this->db->insertID();

            $this->log('dining', 'Booked table reservation for ' . $name . ' (' . $r['party'] . ' guests, ' . $r['date'] . ' ' . $r['time'] . ').', $this->fnb, $when);

            if ($r['status'] === 'no_show') {
                $this->log('dining', 'Marked reservation #' . $id . ' a no-show.', $this->fnb, $r['date'] . ' 20:00:00');
            } elseif ($r['status'] === 'cancelled') {
                $this->log('dining', 'Cancelled reservation #' . $id . '.', $this->fnb, $when);
            } elseif ($r['status'] === 'completed') {
                $this->log('dining', 'Completed reservation #' . $id . '.', $this->fnb, $r['date'] . ' 21:30:00');
            }
        }
    }

    // ---------------------------------------------------------------
    // POS orders — six settled orders with real folios (mirroring
    // PosOrderModel::settleDirect()) plus four "right now" orders still
    // in service, none of which have a folio yet.
    // ---------------------------------------------------------------

    private function seedPosOrders(): void
    {
        $walkInGuestId = $this->walkInGuestId();

        $settled = [
            ['type' => 'dine_in', 'table' => 'T3', 'date' => '2026-07-28', 'items' => [['Jollof Rice', 2], ['Malta Guinness', 2]], 'method' => 'cash'],
            ['type' => 'takeaway', 'table' => null, 'date' => '2026-07-30', 'items' => [['Banku & Tilapia', 1], ['Coca-Cola', 1]], 'method' => 'mobile_money'],
            ['type' => 'dine_in', 'table' => 'T5', 'date' => '2026-08-02', 'items' => [['Grilled Chicken', 2], ['Fried Plantain', 2], ['Star Beer', 2]], 'method' => 'card'],
            ['type' => 'dine_in', 'table' => 'T6', 'date' => '2026-08-06', 'items' => [['Waakye Special', 4], ['Sobolo', 4]], 'method' => 'cash'],
            ['type' => 'takeaway', 'table' => null, 'date' => '2026-08-09', 'items' => [['Fufu & Light Soup', 1]], 'method' => 'mobile_money'],
            ['type' => 'dine_in', 'table' => 'T8', 'date' => '2026-08-10', 'items' => [['Vegetable Fried Rice', 1], ['Fresh Pineapple Juice', 1]], 'method' => 'card'],
        ];

        foreach ($settled as $o) {
            $openedAt = $o['date'] . ' 13:00:00';
            $paidAt   = $o['date'] . ' 13:45:00';

            $this->db->table('pos_orders')->insert([
                'order_type'      => $o['type'],
                'dining_table_id' => $o['table'] ? $this->tableId[$o['table']] : null,
                'guest_id'        => null,
                'status'          => 'paid',
                'opened_by'       => $this->fnb,
                'created_at'      => $openedAt,
                'updated_at'      => $paidAt,
            ]);
            $orderId = (int) $this->db->insertID();
            $this->log('pos', 'Opened ' . $o['type'] . ' order #' . $orderId . '.', $this->fnb, $openedAt);
            $this->log('pos', 'Sent order #' . $orderId . ' to kitchen.', $this->fnb, date('Y-m-d H:i:s', strtotime($openedAt . ' +5 minutes')));
            $this->log('pos', 'Marked order #' . $orderId . ' served.', $this->fnb, date('Y-m-d H:i:s', strtotime($openedAt . ' +20 minutes')));

            // Standalone folio for the walk-in restaurant guest — the exact
            // "direct settlement" pattern the real PosOrderController uses.
            $this->db->table('folios')->insert([
                'guest_id'   => $walkInGuestId,
                'status'     => 'closed',
                'opened_at'  => $paidAt,
                'closed_at'  => $paidAt,
                'created_at' => $paidAt,
                'updated_at' => $paidAt,
            ]);
            $folioId = (int) $this->db->insertID();

            $total = '0.00';
            foreach ($o['items'] as [$itemName, $qty]) {
                $meta   = $this->menuItemMeta[$itemName];
                $amount = money_multiply($meta['price'], $qty);
                $total  = money_add($total, $amount);

                $this->db->table('pos_order_items')->insert([
                    'pos_order_id'   => $orderId,
                    'menu_item_id'   => $meta['id'],
                    'item_name'      => $itemName,
                    'quantity'       => $qty,
                    'unit_price'     => $meta['price'],
                    'kitchen_status' => 'ready',
                    'created_at'     => $openedAt,
                    'updated_at'     => $paidAt,
                ]);
            }

            $this->db->table('folio_line_items')->insert([
                'folio_id'    => $folioId,
                'description' => 'Restaurant order #' . $orderId,
                'amount'      => $total,
                'category'    => 'restaurant',
                'item_date'   => $o['date'],
                'created_by'  => $this->fnb,
                'created_at'  => $paidAt,
                'updated_at'  => $paidAt,
            ]);
            $this->db->table('folio_payments')->insert([
                'folio_id'    => $folioId,
                'amount'      => $total,
                'is_refund'   => 0,
                'method'      => $o['method'],
                'recorded_by' => $this->fnb,
                'paid_at'     => $paidAt,
                'created_at'  => $paidAt,
            ]);

            $this->db->table('pos_orders')->where('id', $orderId)->update(['folio_id' => $folioId]);
            $this->log('pos', 'Settled order #' . $orderId . ' directly (folio #' . $folioId . ').', $this->fnb, $paidAt);
        }

        // Right now — today's dinner service in progress, no folio yet.
        $inService = [
            ['type' => 'dine_in', 'table' => 'T3', 'status' => 'sent_to_kitchen', 'items' => [['Beef Kebab', 1], ['Star Beer', 1]]],
            ['type' => 'dine_in', 'table' => 'T4', 'status' => 'served', 'items' => [['Red Red', 2], ['Bottled Water', 2]]],
            ['type' => 'takeaway', 'table' => null, 'status' => 'open', 'items' => [['Chicken Wings', 1]]],
            ['type' => 'dine_in', 'table' => 'T9', 'status' => 'served', 'items' => [['Chocolate Cake', 1], ['Ice Cream', 1]]],
        ];

        $openedAt = '2026-08-12 19:15:00';
        foreach ($inService as $o) {
            $this->db->table('pos_orders')->insert([
                'order_type'      => $o['type'],
                'dining_table_id' => $o['table'] ? $this->tableId[$o['table']] : null,
                'status'          => $o['status'],
                'opened_by'       => $this->fnb,
                'created_at'      => $openedAt,
                'updated_at'      => $openedAt,
            ]);
            $orderId = (int) $this->db->insertID();
            $this->log('pos', 'Opened ' . $o['type'] . ' order #' . $orderId . '.', $this->fnb, $openedAt);

            foreach ($o['items'] as [$itemName, $qty]) {
                $meta = $this->menuItemMeta[$itemName];
                $this->db->table('pos_order_items')->insert([
                    'pos_order_id'   => $orderId,
                    'menu_item_id'   => $meta['id'],
                    'item_name'      => $itemName,
                    'quantity'       => $qty,
                    'unit_price'     => $meta['price'],
                    'kitchen_status' => $o['status'] === 'open' ? 'pending' : 'ready',
                    'created_at'     => $openedAt,
                    'updated_at'     => $openedAt,
                ]);
            }

            if ($o['status'] !== 'open') {
                $this->log('pos', 'Sent order #' . $orderId . ' to kitchen.', $this->fnb, $openedAt);
            }
            if ($o['status'] === 'served') {
                $this->log('pos', 'Marked order #' . $orderId . ' served.', $this->fnb, date('Y-m-d H:i:s', strtotime($openedAt . ' +15 minutes')));
            }
        }
    }

    /**
     * The generic "Walk-in Customer" guest record (pre-existing) that
     * standalone restaurant folios attach to — same guest the existing
     * pos_orders #1 folio already uses.
     */
    private function walkInGuestId(): int
    {
        $row = $this->db->table('guests')->select('id')->where('full_name', 'Walk-in Customer')->get()->getRow();

        return (int) $row->id;
    }

    // ---------------------------------------------------------------
    // Notifications — internal-only (no third-party dependency), so
    // these are populated in full, referencing the real rows just
    // created above.
    // ---------------------------------------------------------------

    private function seedNotifications(): void
    {
        $items = [
            ['message' => 'New reservation: Room 301 booked for David Chen (7–9 Aug).', 'link' => 'bookings', 'when' => '2026-08-07 09:10:00', 'read' => 1],
            ['message' => 'Payment received: GHS 900.00 from Comfort Adjei (Room 104).', 'link' => 'bookings', 'when' => '2026-08-06 09:15:00', 'read' => 1],
            ['message' => 'Low stock alert: Toothpaste Kits — 18 remaining (reorder at 50).', 'link' => 'inventory', 'when' => '2026-08-08 08:00:00', 'read' => 1],
            ['message' => 'Maintenance reported: Broken Window Latch — Room 301 (high priority).', 'link' => 'maintenance', 'when' => '2026-08-03 09:05:00', 'read' => 1],
            ['message' => 'Low stock alert: Chicken (kg) — 8kg remaining (reorder at 15).', 'link' => 'inventory', 'when' => '2026-08-09 08:00:00', 'read' => 1],
            ['message' => 'New event inquiry: Frimpong Naming Ceremony (30 Aug, 25 guests).', 'link' => 'events', 'when' => '2026-08-10 11:20:00', 'read' => 1],
            ['message' => 'Corporate account added: Zoomlion Ghana Ltd.', 'link' => 'corporate-accounts', 'when' => '2026-06-18 11:05:00', 'read' => 1],
            ['message' => 'Guest checked in: Michael Osei — Room 999.', 'link' => 'checkin', 'when' => '2026-08-11 14:05:00', 'read' => 0],
            ['message' => 'Housekeeping: Room 102 vacate cleaning in progress.', 'link' => 'housekeeping', 'when' => '2026-08-12 09:16:00', 'read' => 0],
            ['message' => 'Low stock alert: Light Bulbs (LED) — 5 remaining (reorder at 20).', 'link' => 'inventory', 'when' => '2026-08-12 08:00:00', 'read' => 0],
            ['message' => 'New reservation: Room 402 pending deposit for Sarah Johnson (10–14 Sep).', 'link' => 'bookings', 'when' => '2026-08-27 10:20:00', 'read' => 0],
        ];

        foreach ($items as $n) {
            $this->db->table('notifications')->insert([
                'message'    => $n['message'],
                'link'       => site_url($n['link']),
                'is_read'    => $n['read'],
                'created_at' => $n['when'],
            ]);
        }
    }
}
