<?php

namespace Database\Seeders;

use Carbon\Carbon;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;

class CashbookEntriesSeeder extends Seeder
{
    public function run(): void
    {
        if (!Schema::hasTable('cashbook_entries') || !Schema::hasTable('cashbooks')) {
            $this->command?->warn('Missing cashbook tables. Run migrations first.');
            return;
        }

        $userIds = Schema::hasTable('users')
            ? DB::table('users')->pluck('id')->all()
            : [];
        $defaultUserId = $userIds[0] ?? null;

        $clients = Schema::hasTable('clients')
            ? DB::table('clients')->get(['id', 'first_name', 'last_name'])
            : collect();
        $clientIds = $clients->pluck('id')->all();

        if ($clients->isEmpty()) {
            $this->command?->warn('No clients found. Trust accounts and trust entries require clients.');
        }

        $currencyCode = 'ZAR';
        if (Schema::hasTable('currencies')) {
            $currencyCode = DB::table('currencies')->inRandomOrder()->value('currency_code') ?? $currencyCode;
        }

        // Insert 10 trust accounts for random clients
        $trustAccountIds = [];
        if (!empty($clientIds) && Schema::hasTable('client_trust_accounts')) {
            $now = now();
            $trustAccountRows = [];
            for ($i = 0; $i < 10; $i++) {
                $clientId = $clientIds[array_rand($clientIds)];
                $creatorId = $userIds ? $userIds[array_rand($userIds)] : $defaultUserId;
                $trustAccountRows[] = [
                    'client_id' => $clientId,
                    'account_number' => 'TA-' . str_pad((string) ($i + 1), 4, '0', STR_PAD_LEFT) . '-' . Str::upper(Str::random(4)),
                    'currency' => $currencyCode,
                    'status' => 'active',
                    'opened_at' => $now->copy()->subDays(rand(30, 365)),
                    'closed_at' => null,
                    'created_by' => $creatorId,
                    'updated_by' => $creatorId,
                    'created_at' => $now,
                    'updated_at' => $now,
                ];
            }
            DB::table('client_trust_accounts')->insert($trustAccountRows);

            $trustAccountIds = DB::table('client_trust_accounts')
                ->orderByDesc('id')
                ->limit(10)
                ->pluck('id')
                ->all();
        }

        $trustCashbooks = DB::table('cashbooks')->where('is_trust', 1)->pluck('id')->all();
        $businessCashbooks = DB::table('cashbooks')->where('is_trust', 0)->orWhereNull('is_trust')->pluck('id')->all();

        if (empty($trustCashbooks) || empty($businessCashbooks)) {
            $this->command?->warn('Missing trust or business cashbooks. Ensure both types exist for correct distribution.');
        }

        $matters = collect();
        if (Schema::hasTable('matters')) {
            $matters = DB::table('matters')->get(['id', 'matter_number', 'matter_title']);
        }

        $matterIds = $matters->pluck('id')->all();
        $matterIdSet = array_fill_keys($matterIds, true);

        $clientMatterMap = [];
        if (Schema::hasTable('matter_clients')) {
            $pairs = DB::table('matter_clients')->get(['client_id', 'matter_id']);
            foreach ($pairs as $pair) {
                $clientMatterMap[$pair->client_id][] = $pair->matter_id;
            }
        }

        $receipts = Schema::hasTable('client_receipts')
            ? DB::table('client_receipts')->where('status', 'processed')->get([
                'id',
                'receipt_number',
                'receipt_date',
                'client_id',
                'matter_id',
                'total_amount',
                'created_by',
                'updated_by',
            ])
            : collect();

        $invoices = Schema::hasTable('client_invoices')
            ? DB::table('client_invoices')->get([
                'id',
                'invoice_number',
                'invoice_date',
                'due_date',
                'client_id',
                'matter_id',
                'total',
                'created_by',
                'updated_by',
            ])
            : collect();

        $tariffs = Schema::hasTable('tariffs')
            ? DB::table('tariffs')->where('is_active', 1)->get([
                'tariff_id',
                'service_code',
                'service_name',
                'billing_method',
                'rate_amount',
                'billing_increment',
                'lawyer_id',
                'vat_rate',
                'currency_id',
            ])
            : collect();

        $currencyIds = Schema::hasTable('currencies')
            ? DB::table('currencies')->pluck('currency_id')->all()
            : [];
        $defaultCurrencyId = $currencyIds[0] ?? 1;

        $pickRandom = static function (array $items) {
            return $items ? $items[array_rand($items)] : null;
        };

        $insertEntries = function (int $count, array $cashbookIds, bool $isTrust) use (
            $clients,
            $clientIds,
            $trustAccountIds,
            $matterIds,
            $matterIdSet,
            $clientMatterMap,
            $receipts,
            $invoices,
            $userIds,
            $defaultUserId,
            $pickRandom
        ): void {
            if (empty($cashbookIds)) {
                return;
            }

            for ($i = 0; $i < $count; $i++) {
                $userId = $userIds ? $userIds[array_rand($userIds)] : $defaultUserId;

                $transactionType = $isTrust
                    ? ($i % 3 === 0 ? 'transfer' : ($i % 2 === 0 ? 'payment' : 'receipt'))
                    : ($i % 2 === 0 ? 'receipt' : 'payment');

                $clientId = !empty($clientIds) ? $clientIds[array_rand($clientIds)] : null;
                $trustAccountId = $isTrust && !empty($trustAccountIds) ? $trustAccountIds[array_rand($trustAccountIds)] : null;

                $matterId = null;
                if ($clientId && isset($clientMatterMap[$clientId])) {
                    $candidate = $pickRandom($clientMatterMap[$clientId]);
                    $matterId = $candidate && isset($matterIdSet[$candidate]) ? $candidate : null;
                }
                if (!$matterId && !empty($matterIds) && rand(0, 1) === 1) {
                    $matterId = $matterIds[array_rand($matterIds)];
                }

                $reference = 'CB-' . Str::upper(Str::random(6));
                $transactionDate = Carbon::now()->subDays(rand(0, 180))->toDateString();
                $amount = 0.0;
                $description = 'Cashbook transaction';

                if (!$isTrust) {
                    $receipt = $receipts->get($i);
                    if ($receipt) {
                        $reference = $receipt->receipt_number;
                        $transactionDate = $receipt->receipt_date;
                        $amount = (float) $receipt->total_amount;
                        $transactionType = 'receipt';
                        $clientId = $receipt->client_id;
                        $matterId = ($receipt->matter_id && isset($matterIdSet[$receipt->matter_id]))
                            ? $receipt->matter_id
                            : null;
                        $userId = $receipt->created_by ?? $userId;
                        $description = 'Client receipt ' . $receipt->receipt_number;
                    } else {
                        $invoice = $invoices->get($i % max(1, $invoices->count()));
                        if ($invoice) {
                            $reference = $invoice->invoice_number;
                            $transactionDate = $invoice->invoice_date ?? $transactionDate;
                            $amount = (float) $invoice->total;
                            $clientId = $invoice->client_id;
                            $matterId = ($invoice->matter_id && isset($matterIdSet[$invoice->matter_id]))
                                ? $invoice->matter_id
                                : null;
                            $userId = $invoice->created_by ?? $userId;
                            $description = 'Invoice ' . $invoice->invoice_number . ' settlement';
                        }
                    }
                } else {
                    $matterLabel = $matterId ? ('Matter #' . $matterId) : 'General trust';
                    $clientLabel = null;
                    if ($clientId) {
                        $client = $clients->firstWhere('id', $clientId);
                        if ($client) {
                            $clientLabel = trim($client->first_name . ' ' . $client->last_name);
                        }
                    }
                    $description = ($transactionType === 'receipt' ? 'Trust receipt' : ($transactionType === 'payment' ? 'Trust payment' : 'Trust transfer'))
                        . ($clientLabel ? (' for ' . $clientLabel) : '')
                        . ' - ' . $matterLabel;
                    $amount = $invoices->count() > 0
                        ? (float) ($invoices->get($i % $invoices->count())->total ?? 0)
                        : (float) rand(250, 5000);
                }

                if ($amount <= 0) {
                    $amount = (float) rand(250, 5000);
                }

                $journalHeaderId = DB::table('journal_headers')->insertGetId([
                    'journal_date' => $transactionDate,
                    'reference' => $reference,
                    'description' => $description,
                    'source_module' => 'cashbook',
                    'created_by' => $userId,
                    'updated_by' => $userId,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);

                DB::table('cashbook_entries')->insert([
                    'cashbook_id' => $cashbookIds[array_rand($cashbookIds)],
                    'journal_header_id' => $journalHeaderId,
                    'transaction_date' => $transactionDate,
                    'reference' => $reference,
                    'description' => $description,
                    'amount' => $amount,
                    'transaction_type' => $transactionType,
                    'client_id' => $clientId,
                    'matter_id' => $matterId,
                    'trust_account_id' => $trustAccountId,
                    'created_by' => $userId,
                    'updated_by' => $userId,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }
        };

        // 60 trust entries, 30 business entries
        $insertEntries(60, $trustCashbooks, true);
        $insertEntries(30, $businessCashbooks, false);

        // Add 10 quotations, 10 invoices, and 25 receipts based on those invoices
        if (
            Schema::hasTable('client_quotations') &&
            Schema::hasTable('client_quotation_lines') &&
            Schema::hasTable('client_invoices') &&
            Schema::hasTable('client_invoice_lines') &&
            Schema::hasTable('client_receipts') &&
            Schema::hasTable('client_receipt_lines') &&
            !empty($clientIds)
        ) {
            $quoteBaseId = (int) (DB::table('client_quotations')->max('id') ?? 0);
            $invoiceBaseId = (int) (DB::table('client_invoices')->max('id') ?? 0);
            $receiptBaseId = (int) (DB::table('client_receipts')->max('id') ?? 0);

            $createdQuotes = [];
            for ($i = 1; $i <= 10; $i++) {
                $clientId = $clientIds[array_rand($clientIds)];
                $client = $clients->firstWhere('id', $clientId);
                $clientName = $client ? trim($client->first_name . ' ' . $client->last_name) : null;

                $matterId = null;
                if (isset($clientMatterMap[$clientId])) {
                    $candidate = $pickRandom($clientMatterMap[$clientId]);
                    $matterId = $candidate && isset($matterIdSet[$candidate]) ? $candidate : null;
                }
                if (!$matterId && !empty($matterIds)) {
                    $matterId = $matterIds[array_rand($matterIds)];
                }

                $tariff = $tariffs->isNotEmpty() ? $tariffs->random() : null;
                $rate = $tariff ? (float) $tariff->rate_amount : (float) rand(800, 5000);
                $taxRate = $tariff ? (float) $tariff->vat_rate : 0.0;
                $currencyId = $tariff ? (int) $tariff->currency_id : $defaultCurrencyId;
                $lawyerId = $tariff ? $tariff->lawyer_id : null;

                $subtotal = round($rate, 2);
                $taxTotal = round($subtotal * ($taxRate / 100), 2);
                $total = $subtotal + $taxTotal;

                $quotationNumber = 'QT-' . str_pad((string) ($quoteBaseId + $i), 3, '0', STR_PAD_LEFT);
                $quotationDate = Carbon::now()->subDays(rand(10, 120))->toDateString();
                $userId = $userIds ? $userIds[array_rand($userIds)] : $defaultUserId;

                $quotationId = DB::table('client_quotations')->insertGetId([
                    'quotation_number' => $quotationNumber,
                    'quotation_date' => $quotationDate,
                    'client_id' => $clientId,
                    'client_name' => $clientName,
                    'matter_id' => $matterId,
                    'case_id' => null,
                    'responsible_lawyer' => $lawyerId,
                    'validity_days' => 30,
                    'terms' => null,
                    'notes' => null,
                    'scope_of_work' => 'Professional legal services as agreed.',
                    'currency_id' => $currencyId,
                    'subtotal' => $subtotal,
                    'tax_total' => $taxTotal,
                    'discount_total' => 0.0,
                    'total_amount' => $total,
                    'status' => 'sent',
                    'sent_at' => now(),
                    'responded_at' => null,
                    'converted_invoice_id' => null,
                    'created_by' => $userId,
                    'updated_by' => $userId,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);

                $quotationLine = [
                    'quotation_id' => $quotationId,
                    'tariff_id' => $tariff ? $tariff->tariff_id : null,
                    'charge_type' => 'FIXED_FEE',
                    'service_code' => $tariff ? $tariff->service_code : null,
                    'description' => $tariff ? $tariff->service_name : 'General legal consultation and drafting',
                    'quantity' => 1.0000,
                    'unit_price' => $subtotal,
                    'discount_pct' => 0.00,
                    'discount_amount' => 0.00,
                    'tax_rate' => $taxRate,
                    'tax_amount' => $taxTotal,
                    'line_total' => $subtotal,
                    'sort_order' => 0,
                    'created_at' => now(),
                    'updated_at' => now(),
                ];
                if (Schema::hasColumn('client_quotation_lines', 'created_by')) {
                    $quotationLine['created_by'] = $userId;
                }
                if (Schema::hasColumn('client_quotation_lines', 'updated_by')) {
                    $quotationLine['updated_by'] = $userId;
                }
                DB::table('client_quotation_lines')->insert($quotationLine);

                $createdQuotes[] = [
                    'id' => $quotationId,
                    'client_id' => $clientId,
                    'matter_id' => $matterId,
                    'currency_id' => $currencyId,
                    'subtotal' => $subtotal,
                    'tax_total' => $taxTotal,
                    'total' => $total,
                    'lawyer_id' => $lawyerId,
                ];
            }

            $createdInvoices = [];
            foreach ($createdQuotes as $idx => $quote) {
                $invoiceNumber = 'INV-' . str_pad((string) ($invoiceBaseId + $idx + 1), 3, '0', STR_PAD_LEFT);
                $invoiceDate = Carbon::now()->subDays(rand(5, 90))->toDateString();
                $dueDate = Carbon::parse($invoiceDate)->addDays(30)->toDateString();
                $userId = $userIds ? $userIds[array_rand($userIds)] : $defaultUserId;

                $invoiceId = DB::table('client_invoices')->insertGetId([
                    'invoice_number' => $invoiceNumber,
                    'invoice_date' => $invoiceDate,
                    'due_date' => $dueDate,
                    'client_id' => $quote['client_id'],
                    'matter_id' => $quote['matter_id'],
                    'case_id' => null,
                    'case_reference' => null,
                    'responsible_lawyer' => $quote['lawyer_id'],
                    'quotation_id' => $quote['id'],
                    'arrangement_id' => null,
                    'billing_period_from' => null,
                    'billing_period_to' => null,
                    'description' => 'Professional services rendered',
                    'narration' => null,
                    'gl_account' => null,
                    'currency_id' => $quote['currency_id'],
                    'subtotal' => $quote['subtotal'],
                    'disbursement_total' => 0.0,
                    'discount_total' => 0.0,
                    'tax_total' => $quote['tax_total'],
                    'total' => $quote['total'],
                    'amount_paid' => 0.0,
                    'status' => 'sent',
                    'sent_at' => now(),
                    'paid_at' => null,
                    'created_by' => $userId,
                    'updated_by' => $userId,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);

                $tariff = $tariffs->isNotEmpty() ? $tariffs->random() : null;
                $billingMethod = $tariff ? $tariff->billing_method : 'FIXED_FEE';
                if ($billingMethod === 'HOURLY') {
                    $billingMethod = 'FIXED_FEE';
                }
                $chargeType = 'FIXED_FEE';
                $rate = $quote['subtotal'];
                $billingIncrement = $tariff ? (int) $tariff->billing_increment : 60;
                $serviceName = $tariff ? $tariff->service_name : 'General legal services';
                $serviceCode = $tariff ? $tariff->service_code : null;
                $lawyerId = $tariff ? $tariff->lawyer_id : $quote['lawyer_id'];

                DB::table('client_invoice_lines')->insert([
                    'invoice_id' => $invoiceId,
                    'charge_type' => $chargeType,
                    'tariff_id' => $tariff ? $tariff->tariff_id : null,
                    'arrangement_id' => null,
                    'service_code' => $serviceCode,
                    'service_name' => $serviceName,
                    'billing_method' => $billingMethod,
                    'rate_snapshot' => $rate,
                    'billing_increment' => $billingIncrement,
                    'lawyer_id' => $lawyerId,
                    'work_date_from' => null,
                    'work_date_to' => null,
                    'total_raw_minutes' => 0,
                    'total_rounded_minutes' => 0,
                    'quantity' => 1.0000,
                    'base_amount' => $quote['subtotal'],
                    'discount_pct' => 0.00,
                    'discount_amount' => 0.00,
                    'line_total' => $quote['subtotal'],
                    'description' => $serviceName,
                    'sort_order' => 0,
                    'created_by' => $userId,
                    'updated_by' => $userId,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);

                $createdInvoices[] = [
                    'id' => $invoiceId,
                    'invoice_number' => $invoiceNumber,
                    'client_id' => $quote['client_id'],
                    'matter_id' => $quote['matter_id'],
                    'currency_id' => $quote['currency_id'],
                    'total' => $quote['total'],
                    'paid' => 0.0,
                ];
            }

            $invoiceIndex = 0;
            $receiptCount = 25;
            for ($i = 1; $i <= $receiptCount; $i++) {
                if (empty($createdInvoices)) {
                    break;
                }

                $invoiceIndex = $invoiceIndex % count($createdInvoices);
                $invoiceRef = &$createdInvoices[$invoiceIndex];
                $remaining = max(0.0, (float) $invoiceRef['total'] - (float) $invoiceRef['paid']);

                if ($remaining <= 0.0) {
                    $invoiceIndex++;
                    $i--;
                    continue;
                }

                $amount = min($remaining, (float) rand(150, 1200));
                $invoiceRef['paid'] += $amount;

                $receiptNumber = 'RCPT-' . str_pad((string) ($receiptBaseId + $i), 3, '0', STR_PAD_LEFT);
                $receiptDate = Carbon::now()->subDays(rand(1, 60))->toDateString();
                $userId = $userIds ? $userIds[array_rand($userIds)] : $defaultUserId;

                $receiptId = DB::table('client_receipts')->insertGetId([
                    'receipt_number' => $receiptNumber,
                    'receipt_date' => $receiptDate,
                    'client_id' => $invoiceRef['client_id'],
                    'matter_id' => $invoiceRef['matter_id'],
                    'responsible_lawyer' => null,
                    'payment_method' => 'bank_transfer',
                    'payment_reference' => $invoiceRef['invoice_number'],
                    'bank_account' => null,
                    'currency_id' => $invoiceRef['currency_id'],
                    'total_amount' => $amount,
                    'exchange_rate' => 1.000000,
                    'notes' => 'Payment for invoice ' . $invoiceRef['invoice_number'],
                    'status' => 'processed',
                    'reversed_at' => null,
                    'reversal_reason' => null,
                    'created_by' => $userId,
                    'updated_by' => $userId,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);

                DB::table('client_receipt_lines')->insert([
                    'receipt_id' => $receiptId,
                    'invoice_id' => $invoiceRef['id'],
                    'account_id' => null,
                    'description' => 'Invoice payment',
                    'amount_applied' => $amount,
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);

                $invoiceIndex++;
            }

            foreach ($createdInvoices as $invoice) {
                $paid = (float) $invoice['paid'];
                $total = (float) $invoice['total'];
                $status = $paid <= 0.0 ? 'sent' : ($paid + 0.01 >= $total ? 'paid' : 'partial');

                DB::table('client_invoices')->where('id', $invoice['id'])->update([
                    'amount_paid' => $paid,
                    'status' => $status,
                    'paid_at' => $status === 'paid' ? now() : null,
                    'updated_at' => now(),
                ]);
            }
        }
    }
}
