<?php

namespace Modules\Bms\Database\Seeders;

use Illuminate\Database\Seeder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Modules\Bms\Entities\BmsBooking;
use Modules\Bms\Entities\BmsHall;
use Modules\Bms\Entities\BmsLead;
use Modules\Bms\Entities\BmsLeadHall;
use Modules\Bms\Entities\BmsMaintenanceRequest;
use Modules\Bms\Entities\BmsOfficeVisit;
use Modules\Bms\Entities\BmsPackage;
use Modules\Bms\Entities\BmsTask;
use Modules\Bms\Services\Booking\BookingService;
use Modules\Bms\Services\Contracts\AssetServiceContract;
use Modules\Bms\Services\Contracts\ContactServiceContract;
use Modules\Bms\Services\Contracts\PaymentServiceContract;
use Modules\Bms\Services\Maintenance\MaintenanceService;
use Modules\Bms\Services\Transfers\TransferService;

/**
 * Rich, re-runnable mock dataset for testing every screen/state. Everything is
 * tagged so it can be removed cleanly: contacts prefixed "[MOCK]", transfers
 * with "[MOCK]" in notes, extra assets coded "MOCK-". Re-running wipes the
 * previous mock set first (incl. the ERP invoices/payments it raised) then
 * regenerates. Run:  php artisan module:seed Bms --class=BmsMockSeeder
 */
class BmsMockSeeder extends Seeder
{
    protected int $biz;
    protected ?int $loc;
    protected int $user = 1;

    public function run(): void
    {
        $this->biz = (int) (DB::table('business')->min('id') ?? 0);
        if ($this->biz === 0) { $this->command?->warn('[BMS mock] no business'); return; }
        $this->loc = DB::table('business_locations')->where('business_id', $this->biz)->value('id');
        $this->user = (int) (DB::table('users')->where('business_id', $this->biz)->min('id') ?? 1);

        // Make sure the demo catalog (halls/packages/assets/templates) exists.
        (new BmsDemoSeeder())->setContainer($this->container)->setCommand($this->command)->run();

        $this->wipe();

        $halls = BmsHall::where('business_id', $this->biz)->orderBy('id')->get();
        $pkgByHall = BmsPackage::where('business_id', $this->biz)->get()->keyBy('hall_id');
        if ($halls->count() < 5) { $this->command?->warn('[BMS mock] need 5 demo halls'); return; }

        $contacts = app(ContactServiceContract::class);
        $bookings = app(BookingService::class);
        $payments = app(PaymentServiceContract::class);

        // ---- LEADS across every pipeline stage --------------------------
        // [name, phone, stage, event_type, attendees, fnb, venue_pref, budget, area, hallIdx[], visit?]
        $specs = [
            ['Nour Samir',    'wizard_incomplete', 'wedding',   300, 'open_buffet', 'closed', 220000, [0], null],
            ['Mariam Adel',   'wizard_incomplete', 'engagement',120, 'set_menu',    'both',   90000,  [1], null],
            ['Salma & Ahmed', 'wizard_complete',   'wedding',   450, 'open_buffet', 'closed', 260000, [2,0], null],
            ['NileTech Co.',  'wizard_complete',   'corporate', 200, 'set_menu',    'closed', 130000, [1], null],
            ['Yara & Omar',   'qualified_awaiting_contact', 'wedding', 350, 'open_buffet', 'closed', 230000, [0,1], 'scheduled'],
            ['Hana Gala',     'qualified_awaiting_contact', 'birthday', 90, 'high_tea',   'open',   45000,  [4], null],
            ['Layla & Karim', 'contacted',         'wedding',   320, 'open_buffet', 'closed', 210000, [0,1,2], 'done'],
            ['Aya & Hassan',  'contacted',         'engagement',180, 'set_menu',    'both',   110000, [1], 'scheduled'],
            ['Dina & Tarek',  'proposal_sent',     'wedding',   400, 'open_buffet', 'closed', 250000, [2,0], 'done'],
            ['Globex Group',  'proposal_sent',     'corporate', 260, 'set_menu',    'closed', 150000, [0], null],
            ['Rana & Sherif', 'negotiation',       'wedding',   280, 'open_buffet', 'both',   200000, [1,0], 'scheduled'],
            ['Farida Lost',   'lost',              'wedding',   300, 'open_buffet', 'closed', 180000, [3], null],
        ];

        $made = ['leads' => 0, 'visits' => 0];
        foreach ($specs as $i => $s) {
            [$name, $stage, $type, $att, $fnb, $vpref, $budget, $hallIdx, $visit] = $s;
            $lead = $this->makeLead($contacts, $name, $stage, $type, $att, $fnb, $vpref, $budget, $hallIdx, $halls, $pkgByHall, $i);
            $made['leads']++;
            if ($visit) { $this->makeVisit($lead, $visit); $made['visits']++; }
        }

        // ---- WON leads that become BOOKINGS in every money state --------
        $bk = [
            // [name, event_type, hallIdx, days_out, action]   action: paid|partial|due|tentative
            ['Mona & Khaled', 'wedding',    0, 70, 'paid'],
            ['Nada & Ziad',   'wedding',    2, 10, 'partial'],   // event soon → contract/balance tasks overdue
            ['Sara & Amr',    'engagement', 1, 45, 'due'],
            ['Heba & Waleed', 'wedding',    4, 95, 'tentative'],
        ];
        $made['bookings'] = 0; $made['payments'] = 0;
        foreach ($bk as $b) {
            [$name, $type, $hIdx, $daysOut, $action] = $b;
            $lead = $this->makeLead($contacts, $name, 'won', $type, 300, 'open_buffet', 'closed', 240000, [$hIdx], $halls, $pkgByHall, 99);
            $date = Carbon::now()->addDays($daysOut)->toDateString();
            try {
                $booking = $bookings->createFromLead($lead->fresh('selectedHalls'), $date, $this->user);
            } catch (\Throwable $e) { $this->command?->warn('[BMS mock] booking skip: '.$e->getMessage()); continue; }
            $made['bookings']++;

            if ($action !== 'tentative') {
                $bookings->confirm($booking);                 // raises ERP invoice + generates tasks
                $total = (float) $booking->total_amount;
                if ($action === 'paid') {
                    $payments->recordPayment($this->biz, $booking->id, ['amount' => $total, 'method' => 'bank', 'user_id' => $this->user]);
                    $made['payments']++;
                    $this->completeSomeTasks($booking->id, 3);
                } elseif ($action === 'partial') {
                    $payments->recordPayment($this->biz, $booking->id, ['amount' => round($total * 0.5), 'method' => 'cash', 'user_id' => $this->user]);
                    $made['payments']++;
                    $this->completeSomeTasks($booking->id, 1);  // leaves overdue contract/balance tasks
                }
                // 'due' → confirmed, invoice raised, no payment, tasks pending
            }
        }

        // ---- EXTRA ASSETS in varied condition / status / placement ------
        $royal = $halls[0]->id; $crystal = $halls[1]->id; $emerald = $halls[2]->id;
        $assets = app(AssetServiceContract::class);
        $mockAssets = [
            ['MOCK-CHR2', 'Silver Tiffany Chairs', 'مقاعد تيفاني فضية', 'seating',  150, 280,  'good',    'in_use',      $royal],
            ['MOCK-TBL2', 'Glass-Top Tables',      'طاولات زجاجية',     'tables',   25,  1500, 'fair',    'in_use',      $crystal],
            ['MOCK-AVP',  'HD Projector + Screen', 'بروجيكتور HD',      'av',       2,   30000,'worn',    'available',   null],
            ['MOCK-ARCH', 'Floral Ceremony Arch',  'قوس زفاف بالورود',  'decor',    3,   4500, 'good',    'in_use',      $emerald],
            ['MOCK-CHND', 'Crystal Chandelier',    'نجفة كريستال',      'lighting', 6,   9000, 'damaged', 'maintenance', null],
            ['MOCK-DANCE','LED Dance Floor',       'أرضية رقص LED',     'staging',  1,   42000,'good',    'available',   null],
            ['MOCK-RUNNER','Gold Aisle Runners',   'ممرات ذهبية',       'linen',    40,  120,  'fair',    'available',   null],
            ['MOCK-RETIRE','Retired Banquet Chairs','مقاعد قديمة',      'seating',  60,  90,   'retired', 'retired',     null],
        ];
        $made['assets'] = 0;
        foreach ($mockAssets as $a) {
            [$code, $name, $nameAr, $cat, $qty, $val, $cond, $status, $hallId] = $a;
            $assets->register($this->biz, [
                'code' => $code, 'name' => $name, 'name_ar' => $nameAr, 'category' => $cat,
                'quantity' => $qty, 'unit_value' => $val, 'condition' => $cond, 'status' => $status,
                'location_id' => $this->loc, 'hall_id' => $hallId, 'moved_by' => $this->user,
            ]);
            $made['assets']++;
        }

        // ---- TRANSFERS in every status ----------------------------------
        $transfers = app(TransferService::class);
        $made['transfers'] = 0;
        $mockAssetId = DB::table('bms_assets')->where('business_id', $this->biz)->where('code', 'MOCK-DANCE')->value('id');

        // 1) requested (pending acceptance) — consumables + an asset
        $transfers->createRequest($this->biz, [
            'type' => 'warehouse_to_hall', 'from_location_id' => $this->loc, 'to_hall_id' => $royal,
            'notes' => '[MOCK] setup for Mona & Khaled',
            'items' => [
                ['kind' => 'consumable', 'name' => 'Fresh Rose Bouquets', 'quantity' => 60],
                ['kind' => 'consumable', 'name' => 'Pillar Candles', 'quantity' => 120],
                ['kind' => 'asset', 'asset_id' => $mockAssetId, 'name' => 'LED Dance Floor', 'quantity' => 1],
            ],
        ], $this->user);
        $made['transfers']++;

        // 2) accepted — warehouse→hall with a short discrepancy
        $t2 = $transfers->createRequest($this->biz, [
            'type' => 'warehouse_to_hall', 'from_location_id' => $this->loc, 'to_hall_id' => $crystal,
            'notes' => '[MOCK] Aya & Hassan engagement',
            'items' => [
                ['kind' => 'consumable', 'name' => 'Table Centerpieces', 'quantity' => 30],
                ['kind' => 'consumable', 'name' => 'Fairy Light Strands', 'quantity' => 50],
            ],
        ], $this->user);
        $items = $t2->items;
        $acc = [];
        foreach ($items as $idx => $it) { $acc[$it->id] = ['accepted_quantity' => $idx === 0 ? 28 : 50, 'condition' => $idx === 0 ? 'short' : 'good']; }
        $transfers->accept($t2->fresh('items'), $this->user, $acc);
        $made['transfers']++;

        // 3) accepted hall→hall (moves a registered asset)
        $archId = DB::table('bms_assets')->where('business_id', $this->biz)->where('code', 'MOCK-ARCH')->value('id');
        $t3 = $transfers->createRequest($this->biz, [
            'type' => 'hall_to_hall', 'from_hall_id' => $emerald, 'to_hall_id' => $royal,
            'notes' => '[MOCK] move arch Emerald→Royal',
            'items' => [['kind' => 'asset', 'asset_id' => $archId, 'name' => 'Floral Ceremony Arch', 'quantity' => 1]],
        ], $this->user);
        $ai = $t3->items->first();
        $transfers->accept($t3->fresh('items'), $this->user, [$ai->id => ['accepted_quantity' => 1, 'condition' => 'good']]);
        $made['transfers']++;

        // 4) rejected
        $t4 = $transfers->createRequest($this->biz, [
            'type' => 'warehouse_to_hall', 'from_location_id' => $this->loc, 'to_hall_id' => $halls[4]->id,
            'notes' => '[MOCK] Garden Pavilion — wrong items',
            'items' => [['kind' => 'consumable', 'name' => 'Outdoor Heaters', 'quantity' => 8]],
        ], $this->user);
        $transfers->reject($t4, $this->user, 'Heaters not needed for this season');
        $made['transfers']++;

        // ---- MAINTENANCE requests across the workflow -------------------
        $maint = app(MaintenanceService::class);
        $chnd = DB::table('bms_assets')->where('business_id', $this->biz)->where('code', 'MOCK-CHND')->value('id');
        // [title, category, priority, hallId|null(office), assetId|null, target_status]
        $mspecs = [
            ['[MOCK] AC not cooling — main hall', 'hvac', 'urgent', $royal, null, 'assigned'],
            ['[MOCK] Flickering chandelier', 'electrical', 'high', $emerald, $chnd, 'in_progress'],
            ['[MOCK] Leaking faucet in prep kitchen', 'plumbing', 'medium', $crystal, null, 'submitted'],
            ['[MOCK] Torn stage carpet', 'furniture', 'low', $royal, null, 'resolved'],
            ['[MOCK] Office printer jam', 'other', 'medium', null, null, 'submitted'],
            ['[MOCK] Cracked ceiling tile — Garden', 'structural', 'high', $halls[4]->id, null, 'verified'],
        ];
        $flow = [
            'assigned' => ['assigned'], 'in_progress' => ['assigned', 'in_progress'],
            'resolved' => ['assigned', 'in_progress', 'resolved'],
            'verified' => ['assigned', 'in_progress', 'resolved', 'verified'],
        ];
        $made['maintenance'] = 0;
        foreach ($mspecs as $m) {
            [$title, $cat, $prio, $hallId, $assetId, $target] = $m;
            $req = $maint->create($this->biz, [
                'hall_id' => $hallId, 'location_id' => $hallId ? null : $this->loc,
                'asset_id' => $assetId, 'category' => $cat, 'priority' => $prio, 'title' => $title,
            ], $this->user);
            foreach ($flow[$target] ?? [] as $to) { $req = $maint->transition($req->fresh(), $to, $this->user, ucfirst(str_replace('_', ' ', $to)) . ' step'); }
            $made['maintenance']++;
        }
        // make the urgent request overdue for SLA testing
        DB::table('bms_maintenance_requests')->where('business_id', $this->biz)
            ->where('priority', 'urgent')->whereIn('status', BmsMaintenanceRequest::OPEN_STATUSES)
            ->update(['due_at' => now()->subHours(3)]);

        $this->command?->info('[BMS mock] done — ' . json_encode($made));
    }

    protected function makeLead($contacts, string $name, string $stage, string $type, int $att, string $fnb, string $vpref, int $budget, array $hallIdx, $halls, $pkgByHall, int $i): BmsLead
    {
        $contact = $contacts->findOrCreateCustomer($this->biz, [
            'name'  => '[MOCK] ' . $name,
            'phone' => '+20100' . str_pad((string) (1000000 + $i + random_int(0, 8999)), 7, '0', STR_PAD_LEFT),
            'email' => 'mock' . $i . '_' . random_int(100, 999) . '@example.com',
            'source' => 'website',
        ]);

        $lead = BmsLead::create([
            'business_id' => $this->biz, 'contact_id' => $contact->id, 'stage' => $stage,
            'source' => 'website', 'event_type' => $type, 'attendees' => $att,
            'fnb_level' => $fnb, 'venue_preference' => $vpref,
            'budget_min' => (int) ($budget * 0.8), 'budget_max' => $budget,
            'candidate_dates' => [Carbon::now()->addDays(50 + $i)->toDateString(), Carbon::now()->addDays(64 + $i)->toDateString()],
            'area_id' => null, 'last_activity_at' => Carbon::now()->subDays(random_int(0, 14)),
        ]);

        $scores = [100, 92, 84, 71, 63];
        foreach ($hallIdx as $rank => $hi) {
            $hall = $halls[$hi];
            BmsLeadHall::create([
                'business_id' => $this->biz, 'lead_id' => $lead->id, 'hall_id' => $hall->id,
                'package_id' => optional($pkgByHall->get($hall->id))->id, 'score' => $scores[$rank] ?? 60,
            ]);
        }
        return $lead;
    }

    protected function makeVisit(BmsLead $lead, string $status): void
    {
        $date = $status === 'done'
            ? Carbon::now()->subDays(random_int(3, 20))->toDateString()
            : Carbon::now()->addDays(random_int(2, 12))->toDateString();
        BmsOfficeVisit::create([
            'business_id' => $this->biz, 'lead_id' => $lead->id, 'contact_id' => $lead->contact_id,
            'location_id' => $this->loc, 'visit_date' => $date, 'visit_time' => ['11:00', '13:30', '16:00'][random_int(0, 2)],
            'status' => $status, 'created_by' => $this->user,
        ]);
    }

    protected function completeSomeTasks(int $bookingId, int $n): void
    {
        $tasks = BmsTask::where('booking_id', $bookingId)->orderBy('due_at')->take($n)->get();
        foreach ($tasks as $t) {
            $t->update(['status' => 'done', 'completed_at' => Carbon::now()->subDays(random_int(0, 5)), 'completed_by' => $this->user]);
        }
    }

    /** Remove the previous mock set (everything tagged) incl. ERP invoices/payments. */
    protected function wipe(): void
    {
        $biz = $this->biz;
        $contactIds = DB::table('contacts')->where('business_id', $biz)->where('name', 'like', '[MOCK]%')->pluck('id');
        if ($contactIds->isNotEmpty()) {
            $leadIds = DB::table('bms_leads')->whereIn('contact_id', $contactIds)->pluck('id');
            $bookingIds = DB::table('bms_bookings')->whereIn('lead_id', $leadIds)->pluck('id');
            $invoiceIds = DB::table('bms_bookings')->whereIn('id', $bookingIds)->whereNotNull('erp_invoice_id')->pluck('erp_invoice_id');
            $txnIds = DB::table('transactions')->whereIn('contact_id', $contactIds)->pluck('id')->merge($invoiceIds)->unique();
            if ($txnIds->isNotEmpty()) {
                DB::table('transaction_payments')->whereIn('transaction_id', $txnIds)->delete();
                DB::table('account_transactions')->whereIn('transaction_id', $txnIds)->delete();
                DB::table('transactions')->whereIn('id', $txnIds)->delete();
            }
            DB::table('bms_hall_availability')->whereIn('booking_id', $bookingIds)->delete();
            DB::table('bms_tasks')->whereIn('booking_id', $bookingIds)->delete();
            DB::table('bms_booking_halls')->whereIn('booking_id', $bookingIds)->delete();
            DB::table('bms_bookings')->whereIn('id', $bookingIds)->delete();
            DB::table('bms_office_visits')->whereIn('lead_id', $leadIds)->delete();
            DB::table('bms_lead_halls')->whereIn('lead_id', $leadIds)->delete();
            DB::table('bms_leads')->whereIn('id', $leadIds)->delete();
            DB::table('contacts')->whereIn('id', $contactIds)->delete();
        }

        $tIds = DB::table('bms_transfer_requests')->where('business_id', $biz)->where('notes', 'like', '%[MOCK]%')->pluck('id');
        if ($tIds->isNotEmpty()) {
            DB::table('bms_asset_movements')->whereIn('transfer_id', $tIds)->delete();
            DB::table('bms_transfer_items')->whereIn('transfer_id', $tIds)->delete();
            DB::table('bms_transfer_requests')->whereIn('id', $tIds)->delete();
        }

        $aIds = DB::table('bms_assets')->where('business_id', $biz)->where('code', 'like', 'MOCK-%')->pluck('id');
        if ($aIds->isNotEmpty()) {
            DB::table('bms_asset_movements')->whereIn('asset_id', $aIds)->delete();
            DB::table('bms_assets')->whereIn('id', $aIds)->delete();
        }

        $mIds = DB::table('bms_maintenance_requests')->where('business_id', $biz)->where('title', 'like', '[MOCK]%')->pluck('id');
        if ($mIds->isNotEmpty()) {
            DB::table('bms_maintenance_updates')->whereIn('request_id', $mIds)->delete();
            DB::table('bms_maintenance_requests')->whereIn('id', $mIds)->delete();
        }
    }
}
