<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Product;
use App\Models\Branch; 
use App\Models\Sale; 
use Illuminate\Support\Facades\Hash;
use App\Services\SaleService; 
use App\Exceptions\StockException; 
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
use App\Events\SaleCreated;
use App\Events\DebtPaid;
use App\Services\AccountingService;

class PosController extends Controller
{
    protected $saleService; 

    public function __construct(SaleService $saleService)
    {
        $this->saleService = $saleService;
    }

    // ========================================================================
    // 1. LOGIN KASIR 
    // ========================================================================
    public function login(Request $request)
    {
        $request->validate([
            'user_id' => 'required',
            'password' => 'required',
        ]);

        $userId = trim($request->json('user_id'));
        $password = trim($request->json('password'));

        $user = User::where('username', $userId)->first();
        
        if (! $user || ! Hash::check($password, $user->password)) {
             return response()->json(['success' => false, 'message' => 'User ID atau Password salah.'], 401);
        }

        if ($user->role !== 'owner' && $user->role !== 'kasir') {
             return response()->json(['success' => false, 'message' => 'Akses ditolak.'], 403);
        }
        
        if ($user->role !== 'owner' && !$user->branch_id) {
            return response()->json(['success' => false, 'message' => 'Akun tidak punya cabang.'], 403);
        }

        $realBranchName = 'Semua Cabang';
        $realBranchAddr = '-';
        $realBranchPhone = '-';

        if ($user->branch_id) {
            $cabang = Branch::find($user->branch_id);
            if ($cabang) {
                $realBranchName = $cabang->name;
                $realBranchAddr = $cabang->address ?? '-';
                $realBranchPhone = $cabang->phone ?? '-';
            }
        }

        $token = $user->createToken('android_pos')->plainTextToken;
        
        return response()->json([
            'success' => true,
            'user' => [
                'id' => $user->id,
                'name' => $user->name,
                'role' => $user->role,
                'branch_id' => $user->branch_id,
                'branch_name' => $realBranchName, 
                'branch_address' => $realBranchAddr, 
                'branch_phone' => $realBranchPhone,
            ],
            'token' => $token,
        ]);
    }

    // ========================================================================
    // 2. GET PRODUCTS 
    // ========================================================================
    public function getProducts(Request $request)
    {
        try {
            $user = $request->user();
            
            $branchId = ($user->role === 'owner' && $request->filled('branch_id')) 
                        ? $request->query('branch_id') 
                        : $user->branch_id; 

            if (!$branchId) {
                return response()->json(['success' => false, 'message' => 'Branch ID error.'], 400);
            }

            $products = \App\Models\Product::where('is_active', true)
                ->whereIn('product_type', ['trading', 'finished_good'])
                ->whereHas('branches', function($q) use ($branchId) {
                    $q->where('branches.id', $branchId);
                })
                ->with([
                    'prices',  
                    'category', 
                    'stocks' => function($q) use ($branchId) {
                        $q->where('branch_id', $branchId);
                    }
                ]) 
                ->get()
                ->map(function ($product) { 
                    
                    // --- MENGAMBIL STOK ---
                    $stockData = $product->stocks->first();
                    $actualStock = 0;
                    if ($stockData) {
                        $actualStock = (float)($stockData->quantity - ($stockData->reserved_qty ?? 0));
                    } else {
                        // FIX PHP 8.3: Jangan panggil properti yang tidak ada di tabel!
                        $actualStock = (float)($product->stock_qty ?? 0);
                    }

                    // --- MENGAMBIL HARGA ---
                    $rawPrices = $product->prices;
                    
                    if ($rawPrices->isEmpty()) {
                        $rawPrices = collect([
                            [ 
                                'level' => '1_user',
                                'amount' => (float)($product->buy_price ?? 0),
                                'unit_level' => 1
                            ]
                        ]);
                    }

                    return [
                        'id' => $product->id,
                        'name' => $product->name,
                        'sku' => $product->sku,
                        'variant_name' => $product->variant_name, 
                        'variant_group_id' => $product->variant_group_id,
                        'category' => $product->category ? $product->category->name : 'Lainnya',
                        'image_url' => $product->image_path ? asset('storage/' . $product->image_path) : null,
                        'unit' => $product->unit ?? 'PCS',
                        
                        'current_stock_qty' => $actualStock, 
                        
                        'prices' => $rawPrices->map(function($p) {
                            $pArr = is_array($p) ? $p : $p->toArray();
                            return [
                                'level' => (string)($pArr['level'] ?? '1_user'), 
                                'amount' => (float)($pArr['amount'] ?? 0),
                                'unit_level' => (int)($pArr['unit_level'] ?? 1) 
                            ];
                        })->values()->all(), 
                        
                        'price_ref_level' => (int)($product->price_reference_level ?? 1), 
                        
                        'conversions' => [
                            'level_2' => [
                                'name' => $product->buy_unit ?? null, 
                                'qty' => (float)($product->conversion_factor ?? 1.0),
                                'price' => 0, 
                            ],
                            'level_3' => [
                                'name' => $product->unit_3_name ?? null, 
                                'qty' => (float)($product->conversion_3_qty ?? 1.0),
                                'price' => 0, 
                            ],
                            'level_4' => [
                                'name' => $product->unit_4_name ?? null, 
                                'qty' => (float)($product->conversion_4_qty ?? 1.0),
                                'price' => 0, 
                            ],
                        ]
                    ];
                });

            return response()->json([
                'success' => true, 
                'total_items' => $products->count(), 
                'data' => $products->values()->all() 
            ]);

        } catch (\Throwable $e) {
            // =======================================================
            // JURUS PAMUNGKAS: Ubah Error PHP menjadi Produk Bohongan!
            // =======================================================
            return response()->json([
                'success' => true, 
                'total_items' => 1, 
                'data' => [
                    [
                        'id' => 999999,
                        'name' => 'ERROR PHP: ' . $e->getMessage() . ' | Baris: ' . $e->getLine(),
                        'sku' => 'BUG-CRASH',
                        'variant_name' => null,
                        'variant_group_id' => null,
                        'category' => 'SYSTEM ERROR',
                        'image_url' => null,
                        'unit' => 'PCS',
                        'current_stock_qty' => 0,
                        'prices' => [
                            ['level' => '1_user', 'amount' => 0, 'unit_level' => 1]
                        ],
                        'price_ref_level' => 1,
                        'conversions' => [
                            'level_2' => ['name' => 'BOX', 'qty' => 1, 'price' => 0],
                            'level_3' => ['name' => 'BAL', 'qty' => 1, 'price' => 0],
                            'level_4' => ['name' => 'DUS', 'qty' => 1, 'price' => 0],
                        ]
                    ]
                ]
            ]);
        }
    }
    
    // ========================================================================
    // 3. SIMPAN TRANSAKSI (Jurnal otomatis via Event Listener & Direct ByPass)
    // ========================================================================
    public function storeTransaction(Request $request)
    {
        $request->validate([
            'grand_total' => 'required|numeric|min:0',
            'items' => 'required|array|min:1',
            'payment_method' => 'required|string', 
        ]);
        
        $user = $request->user();
        $branchId = $user->branch_id;

        // =====================================================================
        // PERBAIKAN: Intercept (Cegat) tipe pembayaran dari Flutter POS
        // Jika dari Flutter tertulis 'credit', kita ubah paksa menjadi 'kredit'
        // =====================================================================
        $metodeBayar = strtolower($request->payment_method);
        if ($metodeBayar === 'credit') {
            $metodeBayar = 'kredit';
        }

        if ($metodeBayar === 'kredit') {
            if (empty($request->customer_id)) {
                return response()->json([
                    'success' => false, 
                    'message' => 'Transaksi Kredit/Hutang WAJIB memilih Nama Pelanggan!'
                ], 400);
            }

            $customer = \App\Models\Customer::find($request->customer_id);
            if ($customer && !$customer->is_active) {
                return response()->json([
                    'success' => false, 
                    'message' => 'TRANSAKSI DITOLAK: Akun pelanggan ini sedang dibekukan/non-aktif. Tidak bisa melakukan hutang.'
                ], 400);
            }
        }

        $invoiceNumber = $this->generateInvoiceNumber($branchId);
        $paymentStatus = ($metodeBayar === 'kredit') ? 'pending' : 'paid';

        $saleData = [
            'branch_id' => $branchId, 
            'user_id' => $user->id,
            'invoice_number' => $invoiceNumber, 
            'subtotal' => $request->subtotal,
            'discount_amount' => $request->discount_amount ?? 0,
            'tax_amount' => $request->tax_amount ?? 0,
            'grand_total' => $request->grand_total, 
            'payment_method' => $metodeBayar, // MENGGUNAKAN VARIABEL YANG SUDAH DITERJEMAHKAN
            'payment_status' => $paymentStatus, 
            'customer_id' => $request->customer_id ?? null,
            'customer_name' => $request->customer_name ?? 'Umum',
            'shipping_address' => $request->shipping_address ?? null, 
            'due_date' => ($paymentStatus === 'pending') ? Carbon::now()->addDays(30) : null,
        ];

        $itemData = [];
        foreach ($request->items as $item) {
            $product = Product::findOrFail($item['product_id']);
            $unitName = $item['unit'] ?? $item['satuan'] ?? $product->unit;
            $conversionFactor = $item['base_conversion_factor'] ?? 1;

            if (!$conversionFactor || $conversionFactor <= 0) {
                 if ($unitName == $product->buy_unit) $conversionFactor = $product->conversion_factor;
                 elseif ($unitName == $product->unit_3_name) $conversionFactor = $product->conversion_3_qty;
                 elseif ($unitName == $product->unit_4_name) $conversionFactor = $product->conversion_4_qty;
                 else $conversionFactor = 1;
            }

            $itemData[] = [
                'product_id' => $item['product_id'],
                'product' => $product,
                'quantity' => $item['quantity'],
                'unit' => $unitName, 
                'base_conversion_factor' => (float) $conversionFactor, 
                'unit_price' => $item['unit_price'], 
                'total_price' => $item['total_price'], 
                'applied_price_level' => $item['applied_price_level'] ?? null, 
            ];
        }
    
        try {
            $sale = $this->saleService->createSale($saleData, $itemData);
            $sale->load(['user', 'branch', 'items']);
            
            // Dispatch untuk kebutuhan Notifikasi (Telegram dll)
            \App\Events\SaleCreated::dispatch($sale);
            
            // =========================================================================
            // PERBAIKAN ENTERPRISE: JURNAL OTOMATIS DIRECT (BYPASS LISTENER KOSONG)
            // =========================================================================
            try {
                $accReceivable = AccountingService::getAccountId('receivable_account');
                $accSalesRevenue = AccountingService::getAccountId('sales_revenue_account');
                $accPpnOutput = AccountingService::getAccountId('ppn_output_account');
                $accCostOfGoodsSold = AccountingService::getAccountId('cost_of_goods_sold_account');
                $accInventory = AccountingService::getAccountId('inventory_account');
                
                // Ambil akun Kas Laci cabang
                $akunKas = DB::table('branch_accounts')
                    ->where('branch_id', $branchId)
                    ->where('type', 'kas_kecil')
                    ->where('is_active', 1)
                    ->value('account_id');
                    
                // Fallback jika kas laci cabang belum di-set, pakai kas global
                if (!$akunKas) {
                    $akunKas = AccountingService::getAccountId('cash_sales_account');
                }

                if ($accReceivable && $accSalesRevenue && $accCostOfGoodsSold && $accInventory && $akunKas) {
                    // Tentukan Debit: Jika Kredit masuk ke Piutang, jika Cash masuk ke Kas Laci
                    $debitAccount = ($metodeBayar === 'kredit') ? $accReceivable : $akunKas;
                    
                    $dpp = $sale->grand_total - ($sale->tax_amount ?? 0);
                    
                    // Hitung total HPP
                    $totalCogs = 0;
                    foreach ($itemData as $item) {
                        $productObj = clone $item['product']; 
                        $actualQty = $item['quantity'] * $item['base_conversion_factor'];
                        $hpp = (isset($productObj->average_cost) && $productObj->average_cost > 0) ? (float) $productObj->average_cost : (float) ($productObj->buy_price ?? 0);
                        $totalCogs += ($hpp * $actualQty);
                    }

                    $journalItems = [];
                    // 1. Piutang / Kas (Debit)
                    $journalItems[] = ['account_id' => $debitAccount, 'debit' => $sale->grand_total, 'credit' => 0];
                    
                    // 2. PPN Keluaran (Kredit)
                    if ($sale->tax_amount > 0) {
                        $journalItems[] = ['account_id' => $accPpnOutput, 'debit' => 0, 'credit' => $sale->tax_amount];
                    }
                    
                    // 3. Pendapatan Penjualan (Kredit)
                    $journalItems[] = ['account_id' => $accSalesRevenue, 'debit' => 0, 'credit' => $dpp];
                    
                    // 4. HPP (Debit)
                    if ($totalCogs > 0) {
                        $journalItems[] = ['account_id' => $accCostOfGoodsSold, 'debit' => $totalCogs, 'credit' => 0];
                        // 5. Persediaan (Kredit)
                        $journalItems[] = ['account_id' => $accInventory, 'debit' => 0, 'credit' => $totalCogs];
                    }

                    AccountingService::log([
                        'branch_id'   => $branchId,
                        'entry_date'  => Carbon::today()->toDateString(),
                        'description' => 'Penjualan POS (Kasir) - ' . $sale->invoice_number,
                        'reference'   => $sale->invoice_number,
                        'source_type' => 'POS_SALES',
                        'user_id'     => $user->id,
                        'items'       => $journalItems
                    ]);
                }
            } catch (\Exception $e) {
                // Biarkan lolos agar transaksi kasir tidak gagal di layar depan
                \Illuminate\Support\Facades\Log::error('Jurnal POS Gagal Terbuat: ' . $e->getMessage());
            }
            // =========================================================================

            return response()->json([
                'success' => true,
                'message' => 'Transaksi berhasil.',
                'sale_id' => $sale->id,
                'invoice_number' => $sale->invoice_number,
                'status' => $paymentStatus,
            ], 201);

        } catch (StockException $e) {
             return response()->json(['success' => false, 'message' => 'Stok Kurang: ' . $e->getMessage()], 400);
        } catch (\Exception $e) {
            return response()->json(['message' => 'Error Server: ' . $e->getMessage()], 500);
        }
    }

    private function generateInvoiceNumber($branchId)
    {
        $branch = Branch::find($branchId);
        $code = $branch->code ?? 'PST'; 
        $dateCode = Carbon::now()->format('ym');

        $lastSale = Sale::where('branch_id', $branchId)
            ->whereYear('created_at', Carbon::now()->year)
            ->whereMonth('created_at', Carbon::now()->month)
            ->orderBy('id', 'desc')
            ->first();

        $nextSequence = 1;
        if ($lastSale) {
            $parts = explode('/', $lastSale->invoice_number);
            if (count($parts) == 4) {
                $nextSequence = intval($parts[3]) + 1;
            }
        }
        return sprintf("INV/%s/%s/%04d", $code, $dateCode, $nextSequence);
    }

    // ========================================================================
    // 4. RIWAYAT TRANSAKSI
    // ========================================================================
    public function getHistory(Request $request)
    {
        $user = $request->user();
        
        $sales = Sale::with('items') 
            ->where('branch_id', $user->branch_id)
            ->whereDate('created_at', \Carbon\Carbon::today())
            ->orderBy('created_at', 'desc') 
            ->get();

        return response()->json([
            'success' => true,
            'data' => $sales->map(function($sale) {
                return [
                    'invoice_number' => $sale->invoice_number,
                    'date' => $sale->created_at->timezone('Asia/Jakarta')->format('d/m/Y H:i'),
                    'grand_total' => (float) $sale->grand_total,
                    'payment_method' => $sale->payment_method,
                    'items' => $sale->items->map(function($item) {
                        return [
                            'product_name' => $item->product_name,
                            'quantity' => (float) $item->quantity,
                            'unit' => $item->unit ?? 'Unit',
                            'unit_price' => (float) $item->unit_price,
                            'total_price' => (float) $item->total_price,
                        ];
                    })
                ];
            })
        ]);
    }

    // ========================================================================
    // 5. LAPORAN HARIAN (CLOSING) - ISOLASI PER SHIFT
    // ========================================================================
    public function getDailySummary(Request $request)
    {
        $user = $request->user();
        $today = \Carbon\Carbon::today();

        // PERBAIKAN FATAL MYSQL 1525: Hapus orWhere('', '')
        $activeShift = \App\Models\DailyShift::where('branch_id', $user->branch_id)
            ->where(function($q) {
                $q->whereNull('closed_at')
                  ->orWhere('closed_at', '0000-00-00 00:00:00')
                  ->orWhere('closed_at', '1970-01-01 00:00:00');
            })
            ->orderBy('id', 'desc')
            ->first();

        // Jika shift aktif ditemukan, kalkulasi semua transaksi sejak detik shift itu dibuat
        $startTime = $activeShift ? $activeShift->created_at : $today->startOfDay();

        $baseQuery = \App\Models\Sale::where('branch_id', $user->branch_id)
            ->where('created_at', '>=', $startTime);

        $totalCashSales = (clone $baseQuery)->where('payment_method', 'cash')->sum('grand_total');
        $totalNonCash = (clone $baseQuery)->where('payment_method', '!=', 'cash')->sum('grand_total');
        $transactionCount = $baseQuery->count();

        $totalCashRefunds = 0;
        if (class_exists('\App\Models\SaleReturn')) {
            $totalCashRefunds = \App\Models\SaleReturn::where('branch_id', $user->branch_id)
                ->where('created_at', '>=', $startTime)
                ->sum('total_refund'); 
        }

        $openingCash = $activeShift ? (float)$activeShift->opening_cash : 0;

        $totalExpenses = 0;
        if (class_exists('\App\Models\Expense')) {
            $totalExpenses = \App\Models\Expense::where('branch_id', $user->branch_id)
                ->where('created_at', '>=', $startTime)
                ->where('fund_source', 'drawer') 
                ->sum('amount');
        }

        $totalCashInDrawer = $totalCashSales + $openingCash - $totalCashRefunds - $totalExpenses;

        return response()->json([
            'success' => true,
            'data' => [
                'date' => $activeShift ? $activeShift->created_at->translatedFormat('l, d F Y') : $today->translatedFormat('l, d F Y'),
                'operator' => $user->name,
                'branch_name' => $user->branch ? $user->branch->name : 'Toko',
                'total_transactions' => $transactionCount,
                'opening_cash' => (float) $openingCash, 
                'sales_cash' => (float) $totalCashSales,
                'total_cash_refunds' => (float) $totalCashRefunds, 
                'total_expenses' => (float) $totalExpenses,
                'total_cash_on_hand' => (float) $totalCashInDrawer, 
                'total_non_cash' => (float) $totalNonCash,
                'total_omzet' => (float) ($totalCashSales + $totalNonCash),
            ]
        ]);
    }

    // ========================================================================
    // 6. FITUR RETUR: CARI NOTA
    // ========================================================================
    public function searchSale(Request $request)
    {
        $user = $request->user();
        
        $branchFilter = function($q) use ($user) {
            if ($user->role !== 'owner') $q->where('branch_id', $user->branch_id);
        };

        $invoiceParam = $request->query('invoice') ?? $request->query('exact_invoice');

        if ($invoiceParam) {
            $sale = Sale::with(['items.product', 'customer'])
                ->where('invoice_number', $invoiceParam)
                ->where($branchFilter)
                ->first();

            if (!$sale) return response()->json(['success' => false, 'message' => 'Nota tidak ditemukan.'], 404);

            return response()->json([
                'success' => true, 
                'mode' => 'detail',
                'data' => [
                    'id' => $sale->id,
                    'invoice_number' => $sale->invoice_number,
                    'date' => $sale->created_at->format('d/m/Y H:i'),
                    'customer' => $sale->customer ? $sale->customer->name : ($sale->customer_name ?? 'Umum'),
                    'items' => $sale->items->map(fn($item) => [
                        'product_id' => $item->product_id,
                        'product_name' => $item->product_name, 
                        'sold_qty' => (float)$item->quantity,
                        'sold_price' => (float)$item->unit_price,
                        'unit' => $item->unit ?? 'Unit', 
                    ])
                ]
            ]);
        }

        $query = Sale::where($branchFilter)->orderBy('created_at', 'desc');

        if ($request->has('date')) {
            $query->whereDate('created_at', $request->query('date'));
        } elseif ($request->has('keyword')) {
            $kw = $request->query('keyword');
            $query->where(function($q) use ($kw) {
                $q->where('invoice_number', 'like', "%$kw%")
                  ->orWhere('customer_name', 'like', "%$kw%");
            });
        } else {
            return response()->json(['success' => false, 'message' => 'Parameter pencarian kurang (invoice/date/keyword).'], 400);
        }

        $sales = $query->limit(50)->get()->map(function($s) {
            return [
                'invoice_number' => $s->invoice_number,
                'time' => $s->created_at->format('H:i'),
                'customer' => $s->customer_name ?? 'Umum',
                'total' => (float) $s->grand_total,
                'item_count' => $s->items()->count(),
            ];
        });

        return response()->json(['success' => true, 'mode' => 'list', 'data' => $sales->values()]);
    }

    // ========================================================================
    // 7. FITUR RETUR: SIMPAN & JURNAL (STRICT MAPPING & BRANCH ISOLATION)
    // ========================================================================
    public function storeReturn(Request $request)
    {
        $request->validate([
            'sale_id' => 'required|exists:sales,id',
            'items' => 'required|array|min:1', 
            'items.*.product_id' => 'required',
            'items.*.qty' => 'required|numeric|min:0.01',
            'items.*.condition' => 'required|in:good,bad',
        ]);

        $user = $request->user();

        try {
            DB::transaction(function () use ($request, $user) {
                
                // PERBAIKAN ENTERPRISE: Cegah retur nota silang cabang
                $sale = Sale::where('branch_id', $user->branch_id)->findOrFail($request->sale_id);
                
                $returnNumber = 'RET-' . time();
                $saleReturn = \App\Models\SaleReturn::create([
                    'sale_id' => $sale->id,
                    'branch_id' => $user->branch_id,
                    'user_id' => $user->id,
                    'return_number' => $returnNumber,
                    'total_refund' => 0, 
                    'reason' => $request->reason ?? '-',
                ]);

                $totalRefund = 0;
                $totalHPP = 0; 

                foreach ($request->items as $itemReq) {
                    $originalItem = \App\Models\SaleItem::where('sale_id', $sale->id)
                        ->where('product_id', $itemReq['product_id'])
                        ->first();

                    if (!$originalItem) continue; 

                    $refundPrice = $originalItem->unit_price * $itemReq['qty'];
                    $totalRefund += $refundPrice;

                    \App\Models\SaleReturnItem::create([
                        'sale_return_id' => $saleReturn->id,
                        'product_id' => $itemReq['product_id'],
                        'quantity' => $itemReq['qty'],
                        'refund_price' => $originalItem->unit_price, 
                        'condition' => $itemReq['condition'],
                    ]);

                    if ($itemReq['condition'] === 'good') {
                        $stockRecord = \App\Models\ProductStock::where('product_id', $itemReq['product_id'])
                            ->where('branch_id', $user->branch_id)
                            ->first();

                        $qtyToReturnBaseUnit = $itemReq['qty'] * ($originalItem->base_conversion_factor ?? 1);

                        // Gunakan average_cost jika ada, fallback ke buy_price
                        $productData = DB::table('products')->where('id', $itemReq['product_id'])->first();
                        $hppPerUnit = (isset($productData->average_cost) && $productData->average_cost > 0) ? $productData->average_cost : ($productData->buy_price ?? 0);
                        $totalHPP += ($hppPerUnit * $qtyToReturnBaseUnit);

                        if ($stockRecord) {
                            $stockRecord->increment('quantity', $qtyToReturnBaseUnit);
                        }

                        \App\Models\StockTransaction::create([
                            'branch_id' => $user->branch_id,
                            'product_id' => $itemReq['product_id'],
                            'type' => 'return', 
                            'input_quantity' => $itemReq['qty'], 
                            'input_unit' => $originalItem->unit, 
                            'actual_quantity' => $qtyToReturnBaseUnit, 
                            'user_id' => $user->id,
                            'note' => 'Retur kondisi Bagus dari Nota ' . $sale->invoice_number,
                        ]);
                    }
                }

                $saleReturn->update(['total_refund' => $totalRefund]);

                // =========================================================================
                // PERBAIKAN ENTERPRISE: STRICT VALIDATION GL & BRANCH MAPPING KAS LACI
                // =========================================================================
                if ($totalRefund > 0) {
                    
                    // Wajib mengambil uang pelunasan retur dari Rekening Kas Laci/Kecil Cabang
                    $akunKas = DB::table('branch_accounts')
                        ->where('branch_id', $user->branch_id)
                        ->where('type', 'kas_kecil')
                        ->where('is_active', 1)
                        ->value('account_id');

                    $akunRetur = AccountingService::getAccountId('retur_penjualan');
                    
                    if (!$akunKas) {
                        throw new \Exception("Akun Kas Laci/Operasional cabang belum dipetakan. Hubungi Pusat.");
                    }
                    if (!$akunRetur) {
                        throw new \Exception("Akun Retur Penjualan belum dikonfigurasi di Master Akun.");
                    }
                    
                    $journalItems = [
                        ['account_id' => $akunRetur, 'debit' => $totalRefund, 'credit' => 0], // Pendapatan berkurang
                        ['account_id' => $akunKas, 'debit' => 0, 'credit' => $totalRefund]    // Kas keluar laci
                    ];

                    if ($totalHPP > 0) {
                        $akunPersediaan = AccountingService::getAccountId('persediaan_barang');
                        $akunHpp = AccountingService::getAccountId('hpp');
                        
                        if (!$akunPersediaan || !$akunHpp) {
                            throw new \Exception("Master Akun Global (Persediaan/HPP) belum dikonfigurasi.");
                        }
                        
                        $journalItems[] = ['account_id' => $akunPersediaan, 'debit' => $totalHPP, 'credit' => 0]; // Stok naik
                        $journalItems[] = ['account_id' => $akunHpp, 'debit' => 0, 'credit' => $totalHPP];        // HPP berkurang
                    }

                    AccountingService::log([
                        'branch_id'   => $user->branch_id,
                        'entry_date'  => Carbon::now()->toDateString(),
                        'description' => "Retur Penjualan POS (Kasir) - " . $sale->invoice_number,
                        'reference'   => $returnNumber,
                        'source_type' => 'SALES_RETURN',
                        'user_id'     => $user->id,
                        'items'       => $journalItems
                    ]);
                }
                // =========================================================================
            });

            return response()->json(['success' => true, 'message' => 'Retur berhasil diproses. Jurnal dan Stok telah diperbarui.']);

        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => 'Error: ' . $e->getMessage()], 500);
        }
    }

    // ========================================================================
    // 8. SHIFT LOGIC (OPEN, CHECK, CLOSE)
    // ========================================================================
    public function openShift(Request $request)
    {
        $request->validate([
            'opening_cash' => 'required|numeric|min:0',
        ]);

        $user = $request->user();
        $today = \Carbon\Carbon::today();

        // PERBAIKAN FATAL MYSQL 1525: Hapus orWhere('', '')
        $activeShift = \App\Models\DailyShift::where('branch_id', $user->branch_id)
            ->where(function($q) {
                $q->whereNull('closed_at')
                  ->orWhere('closed_at', '0000-00-00 00:00:00')
                  ->orWhere('closed_at', '1970-01-01 00:00:00');
            })
            ->first();

        if ($activeShift) {
            return response()->json([
                'success' => false,
                'message' => 'Masih ada shift yang aktif dan belum ditutup. Silakan tutup shift sebelumnya terlebih dahulu.'
            ], 400);
        }

        try {
            // PERBAIKAN FATAL: Memaksa bypass Fillable dan memaksa MySQL merekam nilai "null" 
            $shift = new \App\Models\DailyShift();
            $shift->branch_id = $user->branch_id;
            $shift->user_id = $user->id;
            $shift->date = $today;
            $shift->opening_cash = $request->opening_cash;
            $shift->closed_at = null; // PAKSA NULL
            $shift->actual_closing_cash = null; // PAKSA NULL
            $shift->save();

            return response()->json([
                'success' => true,
                'message' => 'Shift berhasil dibuka.'
            ], 200);

        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => 'Gagal membuka shift: ' . $e->getMessage()
            ], 500);
        }
    }

    public function checkShiftStatus(Request $request)
    {
        try {
            $user = $request->user();
            
            // PERBAIKAN FATAL MYSQL 1525: Hapus orWhere('', '')
            $shift = \App\Models\DailyShift::where('branch_id', $user->branch_id)
                ->where(function($q) {
                    $q->whereNull('closed_at')
                      ->orWhere('closed_at', '0000-00-00 00:00:00')
                      ->orWhere('closed_at', '1970-01-01 00:00:00');
                })
                ->orderBy('id', 'desc')
                ->first();

            if (!$shift) {
                return response()->json([
                    'success' => true,
                    'has_opened_shift' => false,
                ]);
            }

            // Hitung transaksi hanya yang terjadi SETELAH shift dibuat
            $totalCashSales = \App\Models\Sale::where('branch_id', $user->branch_id)
                ->where('created_at', '>=', $shift->created_at) 
                ->where('payment_method', 'cash')
                ->sum('grand_total');

            $totalCashRefunds = 0;
            // PERBAIKAN: Cegah Error 500 jika tabel retur belum ada
            try {
                if (class_exists('\App\Models\SaleReturn')) {
                    $totalCashRefunds = \App\Models\SaleReturn::where('branch_id', $user->branch_id)
                        ->where('created_at', '>=', $shift->created_at)
                        ->sum('total_refund');
                }
            } catch (\Exception $e) {}

            $totalExpenses = 0;
            try {
                if (class_exists('\App\Models\Expense')) {
                    $totalExpenses = \App\Models\Expense::where('branch_id', $user->branch_id)
                        ->where('created_at', '>=', $shift->created_at) 
                        ->where('fund_source', 'drawer')
                        ->sum('amount');
                }
            } catch (\Exception $e) {}

            $expectedCash = $shift->opening_cash + $totalCashSales - $totalCashRefunds - $totalExpenses;

            return response()->json([
                'success' => true,
                'has_opened_shift' => true,
                'shift_data' => [
                    'opened_at' => $shift->created_at ? $shift->created_at->format('Y-m-d H:i:s') : date('Y-m-d H:i:s'),
                    'opening_cash' => (float) $shift->opening_cash,
                    'expected_cash' => (float) $expectedCash,
                ]
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage()
            ], 500);
        }
    }

    public function closeShift(Request $request)
    {
        $request->validate([
            'actual_closing_cash' => 'required|numeric|min:0',
        ]);

        $user = $request->user();

        // PERBAIKAN FATAL MYSQL 1525: Hapus orWhere('', '')
        $shift = \App\Models\DailyShift::where('branch_id', $user->branch_id)
            ->where(function($q) {
                $q->whereNull('closed_at')
                  ->orWhere('closed_at', '0000-00-00 00:00:00')
                  ->orWhere('closed_at', '1970-01-01 00:00:00');
            })
            ->orderBy('id', 'desc')
            ->first();

        if (!$shift) {
            return response()->json([
                'success' => false, 
                'message' => 'Tidak ada shift yang sedang terbuka untuk cabang ini.'
            ], 404);
        }

        $shift->update([
            'actual_closing_cash' => $request->actual_closing_cash,
            'closed_at' => \Carbon\Carbon::now(), 
        ]);

        return response()->json([
            'success' => true, 
            'message' => 'Shift cabang berhasil ditutup dan uang fisik tercatat.'
        ]);
    }

    // ========================================================================
    // 9. STOK OPNAME (MENGGUNAKAN ACCOUNTING SERVICE)
    // ========================================================================
    public function storeStockAdjustment(Request $request)
    {
        $request->validate([
            'product_id' => 'required|exists:products,id',
            'physical_qty' => 'required|numeric', 
            'note' => 'nullable|string',
        ]);

        $user = $request->user();
        $product = Product::findOrFail($request->product_id);

        $currentStockRecord = \App\Models\ProductStock::where('branch_id', $user->branch_id)
            ->where('product_id', $product->id)
            ->first();

        $computerQty = $currentStockRecord ? $currentStockRecord->quantity : 0;
        $physicalQty = $request->physical_qty; 

        $difference = $physicalQty - $computerQty;

        if ($difference == 0) {
            return response()->json(['success' => true, 'message' => 'Stok cocok. Tidak ada perubahan.']);
        }

        try {
            DB::transaction(function () use ($user, $product, $difference, $physicalQty, $request) {
                
                $docNumber = "ADJ-POS-" . time();
                
                \App\Models\StockTransaction::create([
                    'branch_id' => $user->branch_id,
                    'product_id' => $product->id,
                    'type' => 'adjustment', 
                    'input_quantity' => $difference, 
                    'input_unit' => $product->unit,
                    'actual_quantity' => $difference,
                    'user_id' => $user->id,
                    'note' => $request->note ?? 'Stok Opname via Android',
                ]);

                \App\Models\ProductStock::updateOrCreate(
                    ['branch_id' => $user->branch_id, 'product_id' => $product->id],
                    ['quantity' => $physicalQty] 
                );

                // =========================================================================
                // PERBAIKAN ENTERPRISE: HAPUS HARDCODE GL
                // =========================================================================
                $hpp = (isset($product->average_cost) && $product->average_cost > 0) ? $product->average_cost : ($product->buy_price ?? 0);
                $varianceValue = abs($difference) * $hpp;
                
                if ($varianceValue > 0) {
                    $akunPersediaan = AccountingService::getAccountId('persediaan_barang');  
                    $akunBebanSelisih = AccountingService::getAccountId('beban_kerusakan_barang'); 
                    $akunPendapatanLain = AccountingService::getAccountId('pendapatan_lain_lain'); 

                    if (!$akunPersediaan || (!$akunBebanSelisih && $difference < 0) || (!$akunPendapatanLain && $difference > 0)) {
                        throw new \Exception("Master Akun Global untuk Persediaan/Selisih Stok belum dikonfigurasi.");
                    }

                    $journalItems = [];

                    if ($difference < 0) {
                        // DEFISIT (Hilang): Debit Beban, Kredit Persediaan
                        $journalItems[] = ['account_id' => $akunBebanSelisih, 'debit' => $varianceValue, 'credit' => 0];
                        $journalItems[] = ['account_id' => $akunPersediaan, 'debit' => 0, 'credit' => $varianceValue];
                    } else {
                        // SURPLUS (Lebih): Debit Persediaan, Kredit Pendapatan Lain-lain
                        $journalItems[] = ['account_id' => $akunPersediaan, 'debit' => $varianceValue, 'credit' => 0];
                        $journalItems[] = ['account_id' => $akunPendapatanLain, 'debit' => 0, 'credit' => $varianceValue];
                    }

                    AccountingService::log([
                        'branch_id'   => $user->branch_id,
                        'entry_date'  => Carbon::now()->toDateString(),
                        'description' => "Penyesuaian Stok POS (Android) - " . $product->name,
                        'reference'   => $docNumber,
                        'source_type' => 'STOCK_ADJUSTMENT',
                        'user_id'     => $user->id,
                        'items'       => $journalItems
                    ]);
                }
                // =========================================================================
            });

            return response()->json(['success' => true, 'message' => 'Stok berhasil disesuaikan dan dijurnal.']);

        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => 'Error: ' . $e->getMessage()], 500);
        }
    }

    // ========================================================================
    // 10. LIST HUTANG PER PELANGGAN
    // ========================================================================
    public function getCustomerDebts(Request $request)
    {
        $request->validate([
            'customer_id' => 'required|exists:customers,id',
        ]);

        $user = $request->user();

        $query = Sale::where('customer_id', $request->customer_id)
            ->where('payment_status', 'pending');

        if ($user->role !== 'owner') {
            $query->where('branch_id', $user->branch_id);
        }

        $debts = $query->orderBy('created_at', 'asc')
            ->get()
            ->map(function ($sale) {
                $totalPaid = \App\Models\DebtPayment::where('sale_id', $sale->id)->sum('amount');
                $remaining = $sale->grand_total - $totalPaid;

                return [
                    'sale_id' => $sale->id,
                    'invoice_number' => $sale->invoice_number,
                    'date' => $sale->created_at->format('d/m/Y'),
                    'total_bill' => (float) $sale->grand_total,
                    'total_paid' => (float) $totalPaid,
                    'remaining' => (float) $remaining,
                    'due_date' => $sale->due_date ? Carbon::parse($sale->due_date)->format('d/m/Y') : '-',
                ];
            });

        return response()->json([
            'success' => true,
            'data' => $debts
        ]);
    }

    // ========================================================================
    // 11. BAYAR HUTANG (CICIL / LUNAS)
    // Jurnal diproses melalui Listener DebtPaid
    // ========================================================================
    public function storeDebtPayment(Request $request)
    {
        $request->validate([
            'sale_id' => 'required|exists:sales,id',
            'amount' => 'required|numeric|min:1',
            'payment_method' => 'required|string', 
        ]);

        $user = $request->user();

        try {
            DB::transaction(function () use ($request, $user) {
                // PERBAIKAN ENTERPRISE: Cegah pelunasan nota cabang lain
                $sale = Sale::where('id', $request->sale_id);
                if ($user->role !== 'owner') {
                    $sale->where('branch_id', $user->branch_id);
                }
                $sale = $sale->firstOrFail();

                // Catatan Keamanan: Karena pembayaran dilakukan lewat Event Listener,
                // Validasi mapping `branch_accounts` untuk penerimaan uang akan 
                // ditangani secara otomatis di dalam Listener \App\Listeners\ProcessDebtPaymentJournal

                $alreadyPaid = \App\Models\DebtPayment::where('sale_id', $sale->id)->sum('amount');
                $remaining = $sale->grand_total - $alreadyPaid;

                if ($request->amount > $remaining) {
                    throw new \Exception("Nominal pembayaran (Rp " . number_format($request->amount) . ") melebihi sisa hutang (Rp " . number_format($remaining) . ").");
                }

                $payment = \App\Models\DebtPayment::create([
                    'sale_id' => $sale->id,
                    'branch_id' => $user->branch_id,
                    'user_id' => $user->id,
                    'amount' => $request->amount,
                    'payment_method' => $request->payment_method,
                    'note' => $request->note ?? 'Pelunasan via Android',
                ]);

                // EVENT LISTENER YANG AKAN MEMBUAT JURNAL:
                \App\Events\DebtPaid::dispatch($payment);

                $newTotalPaid = $alreadyPaid + $request->amount;
                
                if (abs($sale->grand_total - $newTotalPaid) < 100) {
                    $sale->payment_status = 'paid';
                    $sale->save();
                }
            });

            return response()->json(['success' => true, 'message' => 'Pembayaran berhasil disimpan.']);

        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 400);
        }
    }
    
    // ========================================================================
    // 12. RINGKASAN HUTANG 
    // ========================================================================
    public function getDebtSummary(Request $request)
    {
        $user = $request->user();

        $query = Sale::where('payment_status', 'pending');

        if ($user->role !== 'owner') {
            $query->where('branch_id', $user->branch_id);
        }

        $pendingSales = $query->with('customer')->get();
        $summary = [];

        foreach ($pendingSales as $sale) {
            $paid = \App\Models\DebtPayment::where('sale_id', $sale->id)->sum('amount');
            $remaining = $sale->grand_total - $paid;

            if ($remaining > 100) {
                $custId = $sale->customer_id ?? 0;
                
                if (!isset($summary[$custId])) {
                    $summary[$custId] = [
                        'customer_id' => $custId,
                        'customer_name' => $sale->customer ? $sale->customer->name : ($sale->customer_name ?? 'Umum'),
                        'customer_code' => $sale->customer ? $sale->customer->customer_code : '-',
                        'total_debt' => 0,
                        'nota_count' => 0,
                    ];
                }

                $summary[$custId]['total_debt'] += $remaining;
                $summary[$custId]['nota_count'] += 1;
            }
        }

        $result = array_values($summary);
        usort($result, fn($a, $b) => strcmp($a['customer_name'], $b['customer_name']));

        return response()->json([
            'success' => true,
            'data' => $result
        ]);
    }

    // ========================================================================
    // 13. HISTORY PEMBAYARAN PER NOTA
    // ========================================================================
    public function getDebtHistory($saleId, Request $request)
    {
        $user = $request->user();
        $query = \App\Models\DebtPayment::where('sale_id', $saleId);

        if ($user->role !== 'owner') {
            $query->where('branch_id', $user->branch_id);
        }

        $histories = $query->orderBy('created_at', 'desc')->get()->map(function ($pay) {
            return [
                'id' => $pay->id,
                'date' => $pay->created_at->format('d/m/Y H:i'),
                'amount' => (float) $pay->amount,
                'method' => $pay->payment_method,
                'kasir' => $pay->user->name ?? '-',
            ];
        });

        return response()->json(['success' => true, 'data' => $histories]);
    }

    // ========================================================================
    // 14. APP CONFIG 
    // ========================================================================
    public function getAppConfig(Request $request)
    {
        $ppn = \App\Models\Setting::where('key', 'ppn_rate')->value('value') ?? 11;
        
        return response()->json([
            'success' => true,
            'data' => [
                'ppn_rate' => (float) $ppn,
            ]
        ]);
    }

    // ========================================================================
    // 15. VOID TRANSAKSI KASIR (POS)
    // ========================================================================
    public function voidSale(Request $request, $id)
    {
        $request->validate([
            'reason' => 'required|string'
        ]);

        $user = $request->user();

        DB::beginTransaction();
        try {
            $sale = Sale::where('id', $id)->lockForUpdate()->first();
            
            if (!$sale) throw new \Exception("Transaksi tidak ditemukan.");
            if ($user->role !== 'owner' && $user->role !== 'super_admin' && $sale->branch_id !== $user->branch_id) {
                throw new \Exception("Akses ditolak. Transaksi ini milik cabang lain.");
            }
            if ($sale->payment_status === 'void' || $sale->delivery_status === 'void') {
                throw new \Exception("Transaksi ini sudah dibatalkan sebelumnya.");
            }

            $totalCogs = 0;
            $items = DB::table('sale_items')->where('sale_id', $sale->id)->get();
            
            foreach ($items as $item) {
                $actualQty = $item->quantity * $item->base_conversion_factor;
                
                $product = DB::table('products')->where('id', $item->product_id)->first();
                if ($product) {
                    $hpp = (isset($product->average_cost) && $product->average_cost > 0) ? (float) $product->average_cost : (float) ($product->buy_price ?? 0);
                    $totalCogs += ($hpp * $actualQty);
                }

                $stock = DB::table('product_stocks')
                            ->where('branch_id', $sale->branch_id)
                            ->where('product_id', $item->product_id)
                            ->first();

                if ($stock) {
                    DB::table('product_stocks')->where('id', $stock->id)->update([
                        'quantity' => DB::raw("quantity + $actualQty")
                    ]);
                }

                DB::table('stock_transactions')->insert([
                    'branch_id' => $sale->branch_id,
                    'product_id' => $item->product_id,
                    'type' => 'sale_void',
                    'input_quantity' => $item->quantity,
                    'input_unit' => $item->unit,
                    'actual_quantity' => $actualQty,
                    'user_id' => $user->id,
                    'note' => 'VOID Kasir (Nota: ' . $sale->invoice_number . ') - ' . $request->reason,
                    'created_at' => Carbon::now(),
                    'updated_at' => Carbon::now()
                ]);
            }

            // Jurnal Pembalik
            $accReceivable = AccountingService::getAccountId('receivable_account');
            $accSalesRevenue = AccountingService::getAccountId('sales_revenue_account');
            $accPpnOutput = AccountingService::getAccountId('ppn_output_account');
            $accCostOfGoodsSold = AccountingService::getAccountId('cost_of_goods_sold_account');
            $accInventory = AccountingService::getAccountId('inventory_account');
            $accCashSales = AccountingService::getAccountId('cash_sales_account'); 

            if ($accReceivable && $accSalesRevenue && $accCostOfGoodsSold && $accInventory && $accCashSales) {
                $debitAccount = $sale->payment_method === 'cash' ? $accCashSales : $accReceivable;
                $dpp = $sale->grand_total - ($sale->tax_amount ?? 0);

                $reversalItems = [];
                $reversalItems[] = ['account_id' => $debitAccount, 'debit' => 0, 'credit' => $sale->grand_total];
                if ($sale->tax_amount > 0) {
                    $reversalItems[] = ['account_id' => $accPpnOutput, 'debit' => $sale->tax_amount, 'credit' => 0];
                }
                $reversalItems[] = ['account_id' => $accSalesRevenue, 'debit' => $dpp, 'credit' => 0];
                $reversalItems[] = ['account_id' => $accCostOfGoodsSold, 'debit' => 0, 'credit' => $totalCogs];
                $reversalItems[] = ['account_id' => $accInventory, 'debit' => $totalCogs, 'credit' => 0];

                AccountingService::log([
                    'branch_id'   => $sale->branch_id,
                    'entry_date'  => Carbon::today()->toDateString(),
                    'description' => 'VOID Penjualan Kasir ' . $sale->invoice_number,
                    'reference'   => $sale->invoice_number . '-VOID',
                    'source_type' => 'SALE_POS_VOID',
                    'user_id'     => $user->id,
                    'items'       => $reversalItems
                ]);
            }

            $sale->update([
                'payment_status' => 'void',
                'delivery_status' => 'void',
            ]);

            DB::commit();
            return response()->json(['success' => true, 'message' => 'Transaksi kasir berhasil dibatalkan (VOID). Stok dan Jurnal telah dinetralkan.']);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json(['success' => false, 'message' => $e->getMessage()], 400);
        }
    }

    // ========================================================================
    // 16. HISTORY UNTUK WEB ADMIN (Dengan Isolasi Cabang)
    // ========================================================================
    public function getAdminHistory(Request $request)
    {
        try {
            $user = $request->user();
            $branchId = $user->role === 'owner' || $user->role === 'super_admin' ? ($request->query('branch_id') ?? $user->branch_id) : $user->branch_id;

            // Membatasi hanya transaksi yang bukan dari pesanan Canvas / TO (biasanya POS tidak punya sales_order_id)
            $query = Sale::with(['items', 'user'])->whereNull('sales_order_id');

            if (!empty($branchId) && $branchId !== 'all') {
                $query->where('branch_id', $branchId);
            }

            $history = $query->orderBy('created_at', 'desc')->limit(500)->get()->map(function($sale) {
                return [
                    'id' => $sale->id,
                    'invoice_number' => $sale->invoice_number,
                    'customer_name' => $sale->customer_name ?? 'Umum',
                    'grand_total' => (float) $sale->grand_total,
                    'payment_method' => $sale->payment_method,
                    'payment_status' => $sale->payment_status,
                    'kasir_name' => $sale->user->name ?? 'Kasir',
                    'created_at' => $sale->created_at,
                ];
            });

            return response()->json(['success' => true, 'data' => $history]);
        } catch (\Exception $e) {
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }
}