<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class GeneralLedgerController extends Controller
{
    protected string $module = 'Reports Management';

    public function __construct()
    {
        $module = $this->module;
        $this->middleware("permission:$module General Ledger")->only(['index', 'print']);
    }

    // ════════════════════════════════════════════════════════════════════════
    //  INDEX
    // ════════════════════════════════════════════════════════════════════════
    public function index(Request $request)
    {
        $khataList     = $this->getKhataList();
        $ledgerData    = null;
        $totalDebit    = 0;
        $totalCredit   = 0;
        $selectedKhata = $request->input('khata');   // e.g. "S_5" or "C_12"
        $fromDate      = $request->input('from_date');
        $toDate        = $request->input('to_date');
        $khataType     = null;                        // 'supplier' | 'customer'

        if ($request->filled('khata')) {
            $result      = $this->getLedgerRows($selectedKhata, $fromDate, $toDate);
            $ledgerData  = $result['rows'];
            $totalDebit  = $result['totalDebit'];
            $totalCredit = $result['totalCredit'];
            $khataType   = $result['khataType'];
        }

        return view('reports.general_ledger', compact(
            'khataList',
            'ledgerData',
            'totalDebit',
            'totalCredit',
            'selectedKhata',
            'fromDate',
            'toDate',
            'khataType'
        ));
    }

    // ════════════════════════════════════════════════════════════════════════
    //  PRINT
    // ════════════════════════════════════════════════════════════════════════
    public function print(Request $request)
    {
        $selectedKhata = $request->input('khata');
        $fromDate      = $request->input('from_date');
        $toDate        = $request->input('to_date');

        $result      = $this->getLedgerRows($selectedKhata, $fromDate, $toDate);
        $ledgerData  = $result['rows'];
        $totalDebit  = $result['totalDebit'];
        $totalCredit = $result['totalCredit'];
        $khataType   = $result['khataType'];
        $khataList   = $this->getKhataList();
        $khataName   = $khataList[$selectedKhata] ?? $selectedKhata;

        return view('reports.general_ledger_print_view', compact(
            'ledgerData',
            'totalDebit',
            'totalCredit',
            'selectedKhata',
            'khataName',
            'khataType',
            'fromDate',
            'toDate'
        ));
    }

    // ════════════════════════════════════════════════════════════════════════
    //  CORE LEDGER BUILDER
    // ════════════════════════════════════════════════════════════════════════
    private function getLedgerRows(string $prefixedId, ?string $fromDate, ?string $toDate): array
    {
        // ── Decode prefix ──────────────────────────────────────────────────
        [$type, $rawId] = $this->decodeKhataId($prefixedId);
        // $type  = 'supplier' | 'customer'
        // $rawId = the actual numeric ID stored in adaegi_array → adaegi_khata

        // ── Fetch vouchers (joined with vouchers_unique_number for voucher_type) ──
        //
        //  vouchers_unique_number.voucher_type values expected:
        //    'Receipt Voucher'  → money received FROM customer  (customer ledger)
        //    'Payment Voucher'  → money paid TO supplier        (supplier ledger)
        //
        //  For a customer we only show Receipt Vouchers.
        //  For a supplier  we only show Payment Vouchers.
        //  If voucher_type is NULL / unmatched we still include the row so
        //  nothing is silently lost; filter can be tightened below if needed.
        // ──────────────────────────────────────────────────────────────────
        $query = DB::table('vouchers as v')
            ->leftJoin(
                'vouchers_unique_number as vun',
                'vun.id_vouchers_unique_number',
                '=',
                'v.vouchers_unique_number_id'
            )
            ->whereNotNull('v.adaegi_array')
            ->select(
                'v.id_vouchers',
                'v.voucher_date',
                'v.voucher_details',
                'v.adaegi_array',
                'vun.voucher_type'          // 'Receipt Voucher' | 'Payment Voucher' | null
            );

        if ($fromDate) $query->whereDate('v.voucher_date', '>=', $fromDate);
        if ($toDate)   $query->whereDate('v.voucher_date', '<=', $toDate);

        // ── Restrict by voucher_type per party type ────────────────────────
        if ($type === 'customer') {
            // Receipt Vouchers only → money collected from customers
            $query->where('vun.voucher_type', 'Receipt Voucher');
        } else {
            // Payment Vouchers only → money paid to suppliers
            $query->where('vun.voucher_type', 'Payment Voucher');
        }

        $vouchers = $query->orderBy('v.voucher_date')->orderBy('v.id_vouchers')->get();

        // ── Build rows ─────────────────────────────────────────────────────
        $rows        = [];
        $balance     = 0;
        $totalDebit  = 0;
        $totalCredit = 0;

        // Opening Balance placeholder
        $rows[] = [
            'date'         => $fromDate ? Carbon::parse($fromDate)->format('d/m/y') : '—',
            'detail'       => 'Opening Balance',
            'debit'        => null,
            'credit'       => null,
            'balance'      => 0,
            'dr_cr'        => '',
            'is_opening'   => true,
            'voucher_type' => null,
        ];

        foreach ($vouchers as $voucher) {
            $entries     = json_decode($voucher->adaegi_array, true) ?? [];
            $voucherType = $voucher->voucher_type ?? '';   // 'Receipt Voucher' | 'Payment Voucher'

            foreach ($entries as $entry) {
                // Match the khata entry to our selected party
                if ((string)($entry['adaegi_khata'] ?? '') !== (string)$rawId) continue;

                $debit  = $this->parseAmount($entry['debit_amount']  ?? 0);
                $credit = $this->parseAmount($entry['credit_amount'] ?? 0);

                if ($debit == 0 && $credit == 0) continue;

                // ── Sign / running-balance convention ──────────────────────
                //
                // RECEIPT VOUCHER (customer pays us):
                //   credit_amount in adaegi_array = cash/cheque received from customer
                //   → recorded as CREDIT on customer account (reduces their receivable)
                //   debit_amount  = any adjustment / advance returned to customer
                //   → recorded as DEBIT on customer account (increases receivable)
                //
                // PAYMENT VOUCHER (we pay supplier):
                //   credit_amount = amount owed to supplier (invoice posted)
                //   → increases supplier payable (Cr)
                //   debit_amount  = payment made to supplier
                //   → reduces supplier payable (Dr)
                //
                // Running balance:
                //   positive (+) → Cr  (we owe them / they owe us depending on context)
                //   negative (-) → Dr
                // ──────────────────────────────────────────────────────────
                $balance     += $credit;
                $balance     -= $debit;
                $totalCredit += $credit;
                $totalDebit  += $debit;

                $detail = $entry['description'] ?? $voucher->voucher_details ?? '';

                $rows[] = [
                    'date'         => Carbon::parse($voucher->voucher_date)->format('d/m/y'),
                    'detail'       => $detail,
                    'debit'        => $debit  > 0 ? $debit  : null,
                    'credit'       => $credit > 0 ? $credit : null,
                    'balance'      => abs($balance),
                    'dr_cr'        => $balance > 0 ? 'Cr' : ($balance < 0 ? 'Dr' : ''),
                    'is_opening'   => false,
                    'voucher_type' => $voucherType,
                ];
            }
        }

        return compact('rows', 'totalDebit', 'totalCredit') + ['khataType' => $type];
    }

    // ════════════════════════════════════════════════════════════════════════
    //  KHATA LIST  (suppliers + customers merged, prefixed IDs)
    // ════════════════════════════════════════════════════════════════════════

    /**
     * Returns a flat array:
     *   'S_<id>'  => 'Supplier Name'
     *   'C_<id>'  => 'Customer Name'
     *
     * The prefix prevents ID collisions between the two tables.
     */
    private function getKhataList(): array
    {
        $list = [];

        // ── Suppliers ──────────────────────────────────────────────────────
        if (DB::getSchemaBuilder()->hasTable('suppliers')) {
            $suppliers = DB::table('suppliers')
                ->select('id_supplier', 'supplier_name')
                ->orderBy('supplier_name')
                ->get();

            foreach ($suppliers as $s) {
                $list['S_' . $s->id_supplier] = '(S) ' . $s->supplier_name;
            }
        }

        // ── Customers ──────────────────────────────────────────────────────
        if (DB::getSchemaBuilder()->hasTable('customers')) {
            $customers = DB::table('customers')
                ->select('id_customers', 'customer_name')
                ->whereNull('deleted_at')
                ->where('status', '1')
                ->orderBy('customer_name')
                ->get();

            foreach ($customers as $c) {
                $list['C_' . $c->id_customers] = '(C) ' . $c->customer_name;
            }
        }

        // Fallback: harvest from vouchers if both tables missing
        if (empty($list)) {
            $vouchers = DB::table('vouchers')->whereNotNull('adaegi_array')->get(['adaegi_array']);
            foreach ($vouchers as $v) {
                foreach (json_decode($v->adaegi_array, true) ?? [] as $e) {
                    $id = $e['adaegi_khata'] ?? null;
                    if ($id && !isset($list['S_' . $id])) {
                        $list['S_' . $id] = 'کھاتہ #' . $id;
                    }
                }
            }
            ksort($list);
        }

        return $list;
    }

    // ════════════════════════════════════════════════════════════════════════
    //  HELPERS
    // ════════════════════════════════════════════════════════════════════════

    /**
     * Decode a prefixed khata ID like "S_5" or "C_12".
     * Returns ['supplier'|'customer', '5'|'12']
     */
    private function decodeKhataId(string $prefixedId): array
    {
        if (str_starts_with($prefixedId, 'C_')) {
            return ['customer', substr($prefixedId, 2)];
        }
        // Default / legacy IDs without prefix treated as supplier
        return ['supplier', ltrim($prefixedId, 'S_')];
    }

    private function parseAmount($val): float
    {
        return (float) str_replace(',', '', $val ?? 0);
    }
}