<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
use App\Services\AccountingService;

class AccountPayableController extends Controller
{
    /**
     * 1. GET AKUN KAS & BANK (REVISI ENTERPRISE: BACA DARI MAPPING CABANG)
     */
    public function getAccounts(Request $request)
    {
        $user = $request->user();
        
        // REVISI ENTERPRISE: Izinkan Owner/Pusat untuk memfilter berdasarkan branch_id tertentu (opsional),
        // Namun kunci ketat Branch Admin ke branch_id miliknya sendiri.
        $requestedBranchId = $request->query('branch_id', $user->branch_id ?? 1);
        $branchId = ($user->role !== 'owner' && $user->role !== 'admin_finance') ? $user->branch_id : $requestedBranchId;

        // Tarik akun Kas & Bank hanya yang terdaftar untuk cabang yang dituju
        $accounts = DB::table('accounts')
            ->join('branch_accounts', 'accounts.id', '=', 'branch_accounts.account_id')
            ->where('branch_accounts.branch_id', $branchId)
            ->whereIn('branch_accounts.type', ['kas_kecil', 'kas_besar', 'bank_operasional', 'bank_utama'])
            ->where('accounts.is_active', 1)
            // PENAMBAHAN: Pastikan mapping cabang tersebut statusnya aktif
            ->where('branch_accounts.is_active', 1) 
            ->select('accounts.id', 'accounts.code', 'accounts.name', 'branch_accounts.type')
            ->get();

        // SECURITY FIX ENTERPRISE: 
        // Logika "Fallback" untuk membaca akun secara global (LIKE %Kas% / %Bank%) telah DIHAPUS.
        // Dalam standar ERP, apabila cabang belum dipetakan (mapping) rekening bank-nya oleh Pusat,
        // maka daftar akun harus kosong dan kasir tidak diizinkan membuat transaksi (Force Strict Constraint).
        // Ini menutup celah kebocoran di mana cabang bisa memakai Bank Pusat saat mapping-nya kosong.

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

    /**
     * 2. GET DAFTAR FAKTUR BELUM LUNAS (ANTREAN PEMBAYARAN)
     * FIXED: Mencegah Double Payment dengan memotong Giro Pending & Proteksi Cabang
     */
    public function getPayables(Request $request)
    {
        $user = $request->user();

        $query = DB::table('purchases')
            ->join('suppliers', 'purchases.supplier_id', '=', 'suppliers.id')
            ->where('purchases.status', 'invoiced')
            ->whereIn('purchases.payment_status', ['pending', 'partial', 'unpaid'])
            ->select(
                'purchases.id', 
                'purchases.invoice_number', 
                'purchases.po_number', 
                'purchases.purchase_date', 
                'purchases.due_date', 
                'purchases.grand_total', 
                'purchases.paid_amount', 
                'purchases.dp_amount', 
                'purchases.payment_status', 
                'purchases.branch_id',
                'suppliers.name as supplier_name'
            )
            ->orderBy('purchases.due_date', 'asc');

        // Filter Cabang Otomatis
        if ($user->role !== 'owner' && $user->role !== 'admin_finance') {
            $query->where('purchases.branch_id', $user->branch_id);
        }

        $purchases = $query->get();

        // Mengambil semua Giro yang statusnya masih 'pending'
        $purchaseIds = $purchases->pluck('id')->toArray();
        $pendingGiros = DB::table('debt_payments')
            ->whereIn('purchase_id', $purchaseIds)
            ->where('payment_type', 'giro')
            ->where('giro_status', 'pending')
            ->select('purchase_id', DB::raw('SUM(amount) as total_pending'))
            ->groupBy('purchase_id')
            ->pluck('total_pending', 'purchase_id');

        $filteredData = [];
        
        foreach ($purchases as $row) {
            $row->pending_giro_amount = $pendingGiros[$row->id] ?? 0;
            
            // AUTO-HEAL: Pastikan paid_amount mencakup DP
            $actualPaid = max((float)$row->paid_amount, (float)$row->dp_amount);
            
            // SMART LOGIC: Sisa Hutang harus dikurangi Giro Pending agar tidak ditagih dua kali
            $sisa = (float)$row->grand_total - $actualPaid - (float)$row->pending_giro_amount;
            
            // Hanya tampilkan jika Sisa Hutang > 0
            if ($sisa > 0) {
                $filteredData[] = $row;
            }
        }

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

    /**
     * 3. PROSES PEMBAYARAN (TUNAI / TRANSFER / GIRO MUNDUR)
     */
    public function pay(Request $request)
    {
        $request->validate([
            'purchase_id'  => 'required|integer',
            'amount'       => 'required|numeric|min:1',
            'payment_type' => 'required|in:cash,transfer,giro',
            'account_id'   => 'required|integer',
            'payment_date' => 'required|date'
        ]);

        if ($request->payment_type === 'giro') {
            $request->validate([
                'giro_number'   => 'required|string',
                'giro_due_date' => 'required|date'
            ]);
        }

        DB::beginTransaction();
        try {
            $user = $request->user();
            $purchase = DB::table('purchases')->where('id', $request->purchase_id)->first();
            if (!$purchase) throw new \Exception('Faktur tidak ditemukan.');

            // Proteksi Hak Akses Faktur Cabang
            if ($user->role !== 'owner' && $user->role !== 'admin_finance' && $purchase->branch_id !== $user->branch_id) {
                throw new \Exception('Akses ditolak: Anda tidak memiliki hak akses untuk membayar faktur cabang lain.');
            }

            // PENAMBAHAN: Validasi Otoritas Akun Kas/Bank (Mencegah Tampering ID Akun via API)
            // Memastikan admin tidak membayar hutang menggunakan rekening Owner atau cabang lain
            if ($user->role !== 'owner' && $user->role !== 'admin_finance') {
                $isMapped = DB::table('branch_accounts')
                    ->where('branch_id', $user->branch_id)
                    ->where('account_id', $request->account_id)
                    ->where('is_active', 1) // Tambahan Keamanan: Cek juga apakah mapping masih aktif
                    ->exists();
                
                // SECURITY FIX ENTERPRISE: 
                // Menghapus bypass logika $hasAnyMapping. Jika tidak ada di tabel mapping, 
                // transaksi mutlak ditolak untuk mencegah admin cabang menyisipkan ID Akun Bank Pusat.
                if (!$isMapped) {
                    throw new \Exception('Akses ditolak (Security Lock): Akun Kas/Bank yang Anda pilih belum dipetakan secara resmi untuk wewenang cabang Anda. Silakan hubungi Pusat.');
                }
            }

            // PROTEKSI BACKEND: Validasi Sisa Hutang termasuk memotong Giro Pending
            $pendingGiro = DB::table('debt_payments')
                ->where('purchase_id', $purchase->id)
                ->where('payment_type', 'giro')
                ->where('giro_status', 'pending')
                ->sum('amount');

            $actualPaid = max((float)$purchase->paid_amount, (float)$purchase->dp_amount);
            $sisaHutang = $purchase->grand_total - $actualPaid - $pendingGiro;
            
            if ($request->amount > $sisaHutang) {
                throw new \Exception('Gagal: Nominal bayar melebihi sisa hutang (Sebagian nominal sudah terikat/menunggu kliring Giro).');
            }

            // 1. Catat ke tabel debt_payments
            $paymentId = DB::table('debt_payments')->insertGetId([
                'purchase_id'    => $purchase->id,
                'branch_id'      => $purchase->branch_id,
                'user_id'        => $user->id,
                'amount'         => $request->amount,
                'account_id'     => $request->account_id,
                'payment_method' => $request->payment_type,
                'payment_type'   => $request->payment_type,
                'payment_date'   => $request->payment_date,
                'giro_number'    => $request->giro_number ?? null,
                'giro_due_date'  => $request->giro_due_date ?? null,
                'giro_status'    => $request->payment_type === 'giro' ? 'pending' : null,
                'note'           => $request->note,
                'created_at'     => Carbon::now(),
                'updated_at'     => Carbon::now()
            ]);

            // 2. Persiapan Jurnal Akuntansi via Service Terpusat
            $payableAcc = AccountingService::getAccountId('payable_account') 
                          ?? DB::table('accounts')->where('name', 'like', '%Hutang Dagang%')->value('id');
                          
            if (!$payableAcc) throw new \Exception('Akun Hutang Dagang belum disetting.');

            $journalItems = [];

            if ($request->payment_type === 'giro') {
                // REVISI ENTERPRISE: Hindari DB insertGetId untuk master COA, panggil dari setting
                $giroAccId = AccountingService::getAccountId('giro_payable_account') 
                              ?? DB::table('accounts')->where('name', 'like', '%Hutang Giro%')->value('id');
                             
                if (!$giroAccId) {
                    throw new \Exception('Akun "Hutang Giro Keluar" belum disetting di Master Akun (COA). Harap tambahkan terlebih dahulu.');
                }

                $journalItems[] = ['account_id' => $payableAcc, 'debit' => $request->amount, 'credit' => 0];
                $journalItems[] = ['account_id' => $giroAccId, 'debit' => 0, 'credit' => $request->amount];
                
                $message = 'Giro berhasil dicatat. Status: Menunggu Kliring.';
            } else {
                $journalItems[] = ['account_id' => $payableAcc, 'debit' => $request->amount, 'credit' => 0];
                $journalItems[] = ['account_id' => $request->account_id, 'debit' => 0, 'credit' => $request->amount];

                $newPaid = $actualPaid + $request->amount;
                // Jika pembayaran Tunai + Total Giro + DP >= Grand Total, status paid (di-manage ulang saat giro cair/batal)
                $newStatus = ($newPaid >= $purchase->grand_total) ? 'paid' : 'partial';

                DB::table('purchases')->where('id', $purchase->id)->update([
                    'paid_amount'    => $newPaid,
                    'payment_status' => $newStatus,
                    'updated_at'     => Carbon::now()
                ]);

                $message = 'Pembayaran berhasil. Saldo Kas/Bank telah dipotong.';
            }

            // Eksekusi penulisan jurnal terpusat
            AccountingService::log([
                'branch_id'   => $purchase->branch_id,
                'entry_date'  => $request->payment_date,
                'description' => 'Pelunasan Hutang: ' . $purchase->invoice_number . ' via ' . strtoupper($request->payment_type),
                'reference'   => 'PAY-AP-' . $paymentId,
                'source_type' => 'AP_PAYMENT',
                'user_id'     => $user->id,
                'items'       => $journalItems
            ]);

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

    /**
     * 4. GET DAFTAR GIRO MUNDUR (PENDING)
     */
    public function getPendingGiros(Request $request)
    {
        $user = $request->user();

        $query = DB::table('debt_payments')
            ->join('purchases', 'debt_payments.purchase_id', '=', 'purchases.id')
            ->join('suppliers', 'purchases.supplier_id', '=', 'suppliers.id')
            ->join('accounts', 'debt_payments.account_id', '=', 'accounts.id')
            ->where('debt_payments.payment_type', 'giro')
            ->where('debt_payments.giro_status', 'pending')
            ->select(
                'debt_payments.*', 'purchases.invoice_number', 
                'suppliers.name as supplier_name', 'accounts.name as bank_name'
            )
            ->orderBy('debt_payments.giro_due_date', 'asc');

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

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

    /**
     * 5. CAIRKAN GIRO (KLIRING)
     */
    public function clearGiro(Request $request, $id)
    {
        DB::beginTransaction();
        try {
            $user = $request->user();
            $payment = DB::table('debt_payments')->where('id', $id)->first();
            
            if (!$payment || $payment->giro_status !== 'pending') {
                throw new \Exception('Data Giro tidak valid atau sudah diproses.');
            }

            // Proteksi Hak Akses Cabang
            if ($user->role !== 'owner' && $user->role !== 'admin_finance' && $payment->branch_id !== $user->branch_id) {
                throw new \Exception('Akses ditolak: Anda tidak memiliki hak akses untuk mencairkan giro cabang lain.');
            }

            $purchase = DB::table('purchases')->where('id', $payment->purchase_id)->first();

            DB::table('debt_payments')->where('id', $id)->update([
                'giro_status' => 'cleared',
                'updated_at'  => Carbon::now()
            ]);

            // REVISI ENTERPRISE: Ambil dari setting
            $giroAccId = AccountingService::getAccountId('giro_payable_account') 
                         ?? DB::table('accounts')->where('name', 'like', '%Hutang Giro%')->value('id');

            if (!$giroAccId) throw new \Exception('Akun Hutang Giro tidak ditemukan.');
            
            // Eksekusi penulisan jurnal pencairan terpusat via Service
            AccountingService::log([
                'branch_id'   => $purchase->branch_id,
                'entry_date'  => Carbon::now()->toDateString(),
                'description' => 'Pencairan Giro: ' . $payment->giro_number . ' (Ref: ' . $purchase->invoice_number . ')',
                'reference'   => 'CLR-GIRO-' . $payment->id,
                'source_type' => 'GIRO_CLEARANCE',
                'user_id'     => $user->id,
                'items'       => [
                    ['account_id' => $giroAccId, 'debit' => $payment->amount, 'credit' => 0],
                    ['account_id' => $payment->account_id, 'debit' => 0, 'credit' => $payment->amount]
                ]
            ]);

            $actualPaid = max((float)$purchase->paid_amount, (float)$purchase->dp_amount);
            $newPaid = $actualPaid + $payment->amount;
            $newStatus = ($newPaid >= $purchase->grand_total) ? 'paid' : 'partial';

            DB::table('purchases')->where('id', $purchase->id)->update([
                'paid_amount'    => $newPaid,
                'payment_status' => $newStatus,
                'updated_at'     => Carbon::now()
            ]);

            DB::commit();
            return response()->json(['success' => true, 'message' => 'Giro Berhasil Dicairkan. Saldo Bank telah dipotong.']);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    /**
     * 6. TOLAK GIRO (REJECT)
     */
    public function rejectGiro(Request $request, $id)
    {
        DB::beginTransaction();
        try {
            $user = $request->user();
            $payment = DB::table('debt_payments')->where('id', $id)->first();
            
            if (!$payment || $payment->giro_status !== 'pending') throw new \Exception('Data Giro tidak valid.');

            // Proteksi Hak Akses Cabang
            if ($user->role !== 'owner' && $user->role !== 'admin_finance' && $payment->branch_id !== $user->branch_id) {
                throw new \Exception('Akses ditolak: Anda tidak memiliki hak akses untuk menolak giro cabang lain.');
            }

            $purchase = DB::table('purchases')->where('id', $payment->purchase_id)->first();

            DB::table('debt_payments')->where('id', $id)->update([
                'giro_status' => 'rejected',
                'updated_at'  => Carbon::now()
            ]);

            // REVISI ENTERPRISE
            $giroAccId = AccountingService::getAccountId('giro_payable_account') 
                         ?? DB::table('accounts')->where('name', 'like', '%Hutang Giro%')->value('id');
            $payableAcc = AccountingService::getAccountId('payable_account')
                         ?? DB::table('accounts')->where('name', 'like', '%Hutang Dagang%')->value('id');

            if (!$giroAccId || !$payableAcc) throw new \Exception('Setting akun Giro/Hutang tidak lengkap.');

            // Eksekusi penulisan jurnal penolakan terpusat via Service
            AccountingService::log([
                'branch_id'   => $purchase->branch_id,
                'entry_date'  => Carbon::now()->toDateString(),
                'description' => 'Penolakan Giro: ' . $payment->giro_number,
                'reference'   => 'REJ-GIRO-' . $payment->id,
                'source_type' => 'GIRO_REJECTION',
                'user_id'     => $user->id,
                'items'       => [
                    ['account_id' => $giroAccId, 'debit' => $payment->amount, 'credit' => 0],
                    ['account_id' => $payableAcc, 'debit' => 0, 'credit' => $payment->amount]
                ]
            ]);

            // Jika ada giro ditolak, pastikan payment_status faktur kembali menjadi pending/partial jika uang belum lunas
            $actualPaid = max((float)$purchase->paid_amount, (float)$purchase->dp_amount);
            $newStatus = ($actualPaid >= $purchase->grand_total) ? 'paid' : ($actualPaid > 0 ? 'partial' : 'pending');
            DB::table('purchases')->where('id', $purchase->id)->update([
                'payment_status' => $newStatus,
                'updated_at'     => Carbon::now()
            ]);

            DB::commit();
            return response()->json(['success' => true, 'message' => 'Giro Ditolak. Hutang Dagang dikembalikan.']);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json(['success' => false, 'message' => $e->getMessage()], 500);
        }
    }

    /**
     * 7. GET RIWAYAT PEMBAYARAN
     */
    public function getPaymentHistory(Request $request)
    {
        $user = $request->user();

        $query = DB::table('debt_payments')
            ->join('purchases', 'debt_payments.purchase_id', '=', 'purchases.id')
            ->join('suppliers', 'purchases.supplier_id', '=', 'suppliers.id')
            ->join('accounts', 'debt_payments.account_id', '=', 'accounts.id')
            ->join('users', 'debt_payments.user_id', '=', 'users.id')
            ->whereNotNull('debt_payments.purchase_id')
            ->where(function($q) {
                $q->whereNull('debt_payments.giro_status')
                  ->orWhere('debt_payments.giro_status', 'cleared');
            })
            ->select(
                'debt_payments.*', 'purchases.invoice_number', 'purchases.po_number',
                'suppliers.name as supplier_name', 'accounts.name as bank_name', 'users.name as admin_name'
            )
            ->orderBy('debt_payments.created_at', 'desc');

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

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

    /**
     * 8. VOID (PEMBATALAN) PEMBAYARAN
     */
    public function voidPayment(Request $request, $id)
    {
        DB::beginTransaction();
        try {
            $user = $request->user();
            $payment = DB::table('debt_payments')->where('id', $id)->first();
            
            if (!$payment) {
                throw new \Exception('Data pembayaran tidak ditemukan.');
            }

            // Proteksi Hak Akses Cabang
            if ($user->role !== 'owner' && $user->role !== 'admin_finance' && $payment->branch_id !== $user->branch_id) {
                throw new \Exception('Akses ditolak: Anda tidak memiliki hak akses untuk membatalkan pembayaran cabang lain.');
            }

            $purchase = DB::table('purchases')->where('id', $payment->purchase_id)->first();

            $journals = DB::table('journal_entries')
                ->whereIn('reference', ['PAY-AP-' . $id, 'CLR-GIRO-' . $id])
                ->get();

            foreach($journals as $j) {
                DB::table('journal_items')->where('journal_entry_id', $j->id)->delete();
                DB::table('journal_entries')->where('id', $j->id)->delete();
            }

            if ($payment->payment_type !== 'giro' || $payment->giro_status === 'cleared') {
                $actualPaid = max((float)$purchase->paid_amount, (float)$purchase->dp_amount);
                $newPaid = $actualPaid - $payment->amount;
                
                $newStatus = 'pending';
                if ($newPaid > (float)$purchase->dp_amount) {
                    $newStatus = 'partial';
                }

                DB::table('purchases')->where('id', $purchase->id)->update([
                    'paid_amount'    => $newPaid,
                    'payment_status' => $newStatus,
                    'updated_at'     => Carbon::now()
                ]);
            }

            DB::table('debt_payments')->where('id', $id)->delete();

            DB::commit();
            return response()->json([
                'success' => true, 
                'message' => 'Pembayaran berhasil dibatalkan. Saldo Kas/Bank telah direstorasi dan Hutang kembali terbuka.'
            ]);
        } catch (\Exception $e) {
            DB::rollBack();
            return response()->json([
                'success' => false, 
                'message' => $e->getMessage()
            ], 500);
        }
    }
}