<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use App\XFiles;
use App\SIMBHPJenis;
use App\SIMBHPReport;
use App\User;
use App\Ruangan;
use App\Services\SimbhpStockService;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use Validator;
use Session;

class GudangController extends Controller
{
    public function viewGudangIndex() {
        if (Session::get('previlage') == ''){
            return redirect('/login');
        } else {
            $tasks = [];
            $sekarang = date("Y-m-d");
            $pegawais = User::select('id', 'nama', 'previlage')->orderBy('nama', 'ASC')->get();
            $cdatane = SIMBHPJenis::all();
            $cjenis = count($cdatane);
            if ($cjenis == 0){
                $tasks['jjenis'][0]['id'] = null;
                $tasks['jjenis'][0]['jenis'] = 'Belum Ada Jenis Barang';
                $tasks['jjenis'][0]['kodejenis'] = '';
                $tasks['jjenis'][0]['satuan'] = '';
                $tasks['jjenis'][0]['satuan_kecil'] = '';
                $tasks['jjenis'][0]['konversi_kecil'] = 1;
                $tasks['jjenis'][0]['stok_minimum'] = 0;
            } else {
                $i = 0;
                foreach($cdatane as $rdata){
                    $tasks['jjenis'][$i]['id'] = $rdata->id;
                    $tasks['jjenis'][$i]['jenis'] = $rdata->jenis;
                    $tasks['jjenis'][$i]['kodejenis'] = $rdata->kodejenis ?? '';
                    $tasks['jjenis'][$i]['satuan'] = $rdata->satuan;
                    $tasks['jjenis'][$i]['satuan_kecil'] = $rdata->satuan_kecil ?? '';
                    $tasks['jjenis'][$i]['konversi_kecil'] = $rdata->konversi_kecil ?? 1;
                    $tasks['jjenis'][$i]['stok_minimum'] = $rdata->stok_minimum ?? 0;
                    $i++;
                }
            }
            $getdebet = SIMBHPReport::select(DB::raw('SUM(pemasukan) as pemasukan'))->groupBy('marking')->first();
            if (isset($getdebet->pemasukan)){
                $totpemasukan = $getdebet->pemasukan;
            } else { $totpemasukan = 0 ;}
            $getkredit = SIMBHPReport::select(DB::raw('SUM(pengeluaran) as pengeluaran'))->groupBy('marking')->first();
            if (isset($getkredit->pengeluaran)){
                $totpepengeluaran = $getkredit->pengeluaran;
            } else { $totpepengeluaran = 0 ;}

            $tasks['masuk'] = $totpemasukan;
            $tasks['keluar'] = $totpepengeluaran;
            $tasks['pegawai'] = $pegawais;
            $tasks['tahunne'] = date("Y");
            $tasks['tanggal'] = $sekarang;
            $tasks['sidebar'] = 'simbhp';
            $tasks['stat_harian_masuk'] = $this->getAggregateBaseQty('pemasukan', 'harian');
            $tasks['stat_harian_keluar'] = $this->getAggregateBaseQty('pengeluaran', 'harian');
            $tasks['stat_bulanan_masuk'] = $this->getAggregateBaseQty('pemasukan', 'bulanan');
            $tasks['stat_bulanan_keluar'] = $this->getAggregateBaseQty('pengeluaran', 'bulanan');
            $tasks['stat_tahunan_masuk'] = $this->getAggregateBaseQty('pemasukan', 'tahunan');
            $tasks['stat_tahunan_keluar'] = $this->getAggregateBaseQty('pengeluaran', 'tahunan');
            $tasks['stat_perjenis_harian'] = $this->getUsagePerJenis('harian');
            $tasks['stat_perjenis_bulanan'] = $this->getUsagePerJenis('bulanan');
            $tasks['stat_perjenis_tahunan'] = $this->getUsagePerJenis('tahunan');
            $tasks['warningstok'] = $this->getLowStockWarnings();
            $tasks['expiringSoon'] = $this->getExpiringSoonItems();
            $tasks['jenisRows'] = $this->getJenisRows();
            return view('admin.gudang', $tasks);
        }
    }

    public function jsonRekapbhp() {
        $arraysurat = [];
        $getdata = SIMBHPJenis::all();
        if (!empty($getdata)){
            foreach($getdata as $hasil){
                $jenis = $hasil->kodejenis;
                $satuan = $hasil->satuan;
                $satuanKecil = $hasil->satuan_kecil ?? '';
                $konversi = (int) ($hasil->konversi_kecil ?? 1);
                if ($konversi <= 0) { $konversi = 1; }
                $tlsjenis = $hasil->jenis;
                $saldoBase = $this->getStockBaseByJenis($tlsjenis);
                $saldoakhir = $this->formatStockDisplay($saldoBase, $satuan, $satuanKecil, $konversi);
                $isWarning = $saldoBase <= (int) ($hasil->stok_minimum ?? 0);
                $arraysurat[] = array(
                    'id' => $hasil->id,
                    'satuan' => $satuan,
                    'jenis' => $jenis,
                    'tlsjenis' => $tlsjenis,
                    'saldo' => $saldoakhir,
                    'warning' => $isWarning ? 'YA' : 'TIDAK',
                );
            }
        }
        echo json_encode($arraysurat);
    }

    public function jsonSnapshot()
    {
        if (Session::get('previlage') == '') {
            return response()->json(['message' => 'Unauthorized'], 401);
        }

        $jjenis = [];
        $cdatane = SIMBHPJenis::orderBy('jenis', 'ASC')->get();
        if ($cdatane->count() == 0) {
            $jjenis[] = [
                'id' => null,
                'jenis' => 'Belum Ada Jenis Barang',
                'kodejenis' => '',
                'satuan' => '',
                'satuan_kecil' => '',
                'konversi_kecil' => 1,
                'stok_minimum' => 0,
            ];
        } else {
            foreach ($cdatane as $rdata) {
                $jjenis[] = [
                    'id' => $rdata->id,
                    'jenis' => $rdata->jenis,
                    'kodejenis' => $rdata->kodejenis ?? '',
                    'satuan' => $rdata->satuan,
                    'satuan_kecil' => $rdata->satuan_kecil ?? '',
                    'konversi_kecil' => $rdata->konversi_kecil ?? 1,
                    'stok_minimum' => $rdata->stok_minimum ?? 0,
                ];
            }
        }

        return response()->json([
            'jjenis' => $jjenis,
            'jenisRows' => $this->getJenisRows(),
            'warningstok' => $this->getLowStockWarnings(),
            'expiringSoon' => $this->getExpiringSoonItems(),
            'stat' => [
                'harian' => [
                    'masuk' => $this->getAggregateBaseQty('pemasukan', 'harian'),
                    'keluar' => $this->getAggregateBaseQty('pengeluaran', 'harian'),
                    'perjenis' => $this->getUsagePerJenis('harian'),
                ],
                'bulanan' => [
                    'masuk' => $this->getAggregateBaseQty('pemasukan', 'bulanan'),
                    'keluar' => $this->getAggregateBaseQty('pengeluaran', 'bulanan'),
                    'perjenis' => $this->getUsagePerJenis('bulanan'),
                ],
                'tahunan' => [
                    'masuk' => $this->getAggregateBaseQty('pemasukan', 'tahunan'),
                    'keluar' => $this->getAggregateBaseQty('pengeluaran', 'tahunan'),
                    'perjenis' => $this->getUsagePerJenis('tahunan'),
                ],
            ],
        ]);
    }

    public function jsonReportbhp(Request $request) {
        $mulai = $request->input('val01');
        $akhir = $request->input('val02');
        $hasil = [];
        if ($mulai == $akhir OR $akhir == '' OR $akhir == null){
            $getdata = SIMBHPReport::whereDate('created_at', $mulai)->orderBy('id', 'DESC')->get();
        } else {
            $getdata = SIMBHPReport::whereBetween('created_at', [$mulai, $akhir])->orderBy('id', 'DESC')->get();
        }
        
        foreach($getdata as $rdata){
            $dd                 = $rdata->tanggal;
            $mm                 = $rdata->bulan;
            $yy                 = $rdata->tahun;
            $pengeluaran        = $rdata->pengeluaran;
            $pemasukan          = $rdata->pemasukan;
            $deskripsiGabungan  = trim((string) ($rdata->deskripsi ?? ''));
            $keteranganGabungan = trim((string) ($rdata->keterangan ?? ''));
            if ($keteranganGabungan !== '') {
                $deskripsiGabungan = $deskripsiGabungan !== ''
                    ? $deskripsiGabungan.' | '.$keteranganGabungan
                    : $keteranganGabungan;
            }
            if ($mm < 10){
                $tgllengkap = $dd.'-0'.$mm.'-'.$yy;
            } else {
                $tgllengkap = $dd.'-'.$mm.'-'.$yy;
            }
            if ($pengeluaran == '' OR $pengeluaran == 0) {$total = $pemasukan;}
            else { $total = $pengeluaran; }

            $hasil[] = array(
                'id'            => $rdata->id,
                'tanggal'       => $rdata->tanggal,
                'bulan'         => $rdata->bulan,
                'tahun'         => $rdata->tahun,
                'deskripsi'     => $deskripsiGabungan,
                'pemasukan'     => $rdata->pemasukan ?? 0,
                'pengeluaran'   => $rdata->pengeluaran ?? 0,
                'jenis'         => $rdata->jenis,
                'masa_expired'  => $rdata->masa_expired,
                'keterangan'    => $rdata->keterangan,
                'tgllengkap'    => $rdata->created_at->format('m/d/Y'),
                'total'         => $total,
            );
        }
        echo json_encode($hasil);
    }

    public function exportReportbhp(Request $request)
    {
        $bulan = strtoupper((string) $request->query('bulan', date('m')));
        $tahun = strtoupper((string) $request->query('tahun', date('Y')));

        if ($tahun === 'ALL'){
            $bulan = date("m");
            $tahun = date("Y");
            $query = SIMBHPReport::where('bulan', (int) $bulan)->where('tahun', (int) $tahun);
        } else {
            $query = SIMBHPReport::where('tahun', (int) $tahun);
            if ($bulan !== 'ALL') {
                $query->where('bulan', (int) $bulan);
            }
        }

        $rows = $query->orderBy('id', 'DESC')->get();
        $filename = 'laporan-gudang-bln-'.$bulan.'_thn-'.$tahun.'.xlsx';

        return response()->streamDownload(function () use ($rows) {
            $spreadsheet = new Spreadsheet();
            $sheet = $spreadsheet->getActiveSheet();
            $sheet->setTitle('Laporan Gudang');

            $headers = ['Tanggal', 'Bulan', 'Tahun', 'Jenis', 'Deskripsi', 'Pemasukan', 'Pengeluaran', 'Satuan Transaksi', 'Masa Expired', 'Keterangan'];
            $sheet->fromArray($headers, null, 'A1');

            $rowNumber = 2;
            foreach ($rows as $row) {
                $sheet->fromArray([
                    (int) ($row->tanggal ?? 0),
                    (int) ($row->bulan ?? 0),
                    (int) ($row->tahun ?? 0),
                    $row->jenis ?: '-',
                    $row->deskripsi ?: '-',
                    (int) ($row->pemasukan ?? 0),
                    (int) ($row->pengeluaran ?? 0),
                    $row->satuan_transaksi ?: '-',
                    $row->masa_expired ?: '-',
                    $row->keterangan ?: '-',
                ], null, 'A'.$rowNumber);
                $rowNumber++;
            }

            foreach (range('A', 'J') as $col) {
                $sheet->getColumnDimension($col)->setAutoSize(true);
            }

            $writer = new Xlsx($spreadsheet);
            $writer->save('php://output');
            $spreadsheet->disconnectWorksheets();
            unset($spreadsheet);
        }, $filename, [
            'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
        ]);
    }

    public function jsonReportbhpPaginated(Request $request) {
        $tanggal    = $request->input('tanggal');
        $deskripsi  = $request->input('deskripsi');
        $kategori   = $request->input('kategori');
        $lm         = 10;
        $limit      = ($request->input('limit') == null ? $lm : $request->input('limit'));
        $order      = ($request->input('order') == null ? 'id desc' : $request->input('order'));
        $data       = new SIMBHPReport;
        if ($kategori != null AND $kategori != '') $data = $data->where('jenis', $kategori);
        if ($tanggal != null AND $tanggal != '') $data = $data->where('created_at', 'LIKE', '%'.$tanggal.'%');
        if ($deskripsi != null AND $deskripsi != '') $data = $data->where('deskripsi', 'LIKE', '%'.$deskripsi.'%');
        $data = $data->orderByRaw($order)->paginate($limit);
        $hasil = [];
        $totaldata = $data->total();
        $debet = 0;
        $kredit = 0;
        if (!empty($data)){
            foreach($data as $rdata){
                
                $pengeluaran    = $rdata->pengeluaran;
                $pemasukan      = $rdata->pemasukan;
                $deskripsi      = $rdata->deskripsi;
                $jenis          = $rdata->jenis;
                $debet          = $debet + $pemasukan;
                $kredit         = $kredit + $pengeluaran;
                $cekjenis       = SIMBHPJenis::where('kodejenis', $jenis)->first();
                if (isset($cekjenis->id)){
                    $kodejenis  = $cekjenis->kodejenis;
                    $jenis      = $cekjenis->jenis;
                    $satuan     = $cekjenis->satuan;
                } else {
                    $kodejenis  = $jenis;
                    $jenis      = '';
                    $satuan     = '';
                }

                if ($jenis != ''){
                    $deskripsi = '<strong>'.$jenis.'</strong><br />'.$deskripsi;
                }

                
                if ($pengeluaran == '' OR $pengeluaran == 0) {
                    $total      = $pemasukan;
                    $jentrans   = 'PEMASUKAN';
                }
                else {
                    $total      = $pengeluaran;
                    $jentrans   = 'PENGELUARAN';
                }

                $hasil[] = array(
                    'id'            => $rdata->id,
                    'tanggal'       => $rdata->tanggal,
                    'bulan'         => $rdata->bulan,
                    'tahun'         => $rdata->tahun,
                    'tlsdeskripsi'  => $deskripsi,
                    'deskripsi'     => $rdata->deskripsi,
                    'pemasukan'     => $rdata->pemasukan ?? 0,
                    'pengeluaran'   => $rdata->pengeluaran ?? 0,
                    'jenis'         => $kodejenis,
                    'masa_expired'  => $rdata->masa_expired,
                    'keterangan'    => $rdata->keterangan,
                    'tgllengkap'    => $rdata->created_at->format('m/d/Y'),
                    'created_at'    => $rdata->created_at,
                    'nominal'       => $total.' '.$satuan,
                    'jentrans'      => $jentrans,
                );
            }
        }

        $response = [
            'message' => 'List Data',
            'data' => $hasil,
            'totaldata' => $totaldata
        ];
        return response()->json($response, 200);
    }

    public function jsonExpiredBatches(Request $request)
    {
        if (Session::get('previlage') == '') {
            return response()->json(['message' => 'Unauthorized'], 401);
        }

        $jenis = trim((string) $request->query('jenis', ''));
        $query = SIMBHPReport::whereNotNull('pemasukan')
            ->where('pemasukan', '>', 0);

        if ($jenis !== '') {
            $query->where('jenis', $jenis);
        }

        $rows = $query->orderByRaw('masa_expired IS NULL ASC')
            ->orderBy('masa_expired', 'ASC')
            ->orderBy('id', 'DESC')
            ->limit(100)
            ->get();

        $items = [];
        foreach ($rows as $row) {
            $items[] = [
                'id' => $row->id,
                'jenis' => $row->jenis,
                'deskripsi' => $row->deskripsi ?: '-',
                'tanggal_masuk' => sprintf('%02d-%02d-%04d', (int) $row->tanggal, (int) $row->bulan, (int) $row->tahun),
                'qty' => (int) ($row->pemasukan ?? 0),
                'satuan' => $row->satuan_transaksi ?: 'besar',
                'masa_expired' => $row->masa_expired ? date('d-m-Y', strtotime((string) $row->masa_expired)) : '',
            ];
        }

        return response()->json(['data' => $items]);
    }

    public function updateExpiredBatch(Request $request)
    {
        if (Session::get('previlage') == '') {
            return response()->json(['message' => 'Unauthorized'], 401);
        }

        $batchId = (int) $request->input('batch_id');
        $masaExpired = $this->normalizeDateInput($request->input('masa_expired'));
        $alasan = trim((string) $request->input('alasan', 'Update expired barang terbuka'));

        if ($batchId <= 0 || is_null($masaExpired)) {
            return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Batch dan tanggal expired baru wajib diisi']);
        }

        $row = SIMBHPReport::where('id', $batchId)
            ->whereNotNull('pemasukan')
            ->where('pemasukan', '>', 0)
            ->first();

        if (!$row) {
            return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Batch barang masuk tidak ditemukan']);
        }

        $oldExpired = $row->masa_expired;
        $keterangan = trim((string) ($row->keterangan ?? ''));
        $suffix = 'Expired dibuka: '.($oldExpired ?: '-').' -> '.$masaExpired;
        if ($alasan !== '') {
            $suffix .= ' | '.$alasan;
        }

        $row->masa_expired = $masaExpired;
        $row->keterangan = $keterangan !== '' ? $keterangan.' | '.$suffix : $suffix;
        $row->updated_at = date("Y-m-d H:i:s");
        $row->save();

        return response()->json(['status' => 'Success', 'message' => 'Masa expired batch berhasil diupdate']);
    }

    public function storeRacikan(Request $request)
    {
        if (Session::get('previlage') == '') {
            return response()->json(['message' => 'Unauthorized'], 401);
        }

        $tanggal = trim((string) $request->input('tanggal', date('Y-m-d')));
        $namaRacikan = trim((string) $request->input('nama_racikan', 'Racikan'));
        $statusRacikan = strtolower(trim((string) $request->input('status_racikan', 'berhasil')));
        $bahan = $request->input('bahan', []);
        if (is_string($bahan)) {
            $decoded = json_decode($bahan, true);
            $bahan = is_array($decoded) ? $decoded : [];
        }

        if ($tanggal === '') {
            $tanggal = date('Y-m-d');
        }
        if ($namaRacikan === '') {
            $namaRacikan = 'Racikan';
        }
        if (!in_array($statusRacikan, ['berhasil', 'rusak'], true)) {
            return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Status racikan tidak valid']);
        }
        if (!is_array($bahan) || count($bahan) === 0) {
            return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Minimal satu bahan racikan wajib diisi']);
        }

        $parts = explode('-', $tanggal);
        $tahun = (int) ($parts[0] ?? date('Y'));
        $wulan = (int) ($parts[1] ?? date('m'));
        $dino = (int) ($parts[2] ?? date('d'));
        $marking = 'RACIK-'.date('YmdHis').'-'.random_int(100, 999);

        try {
            DB::transaction(function () use ($request, $bahan, $statusRacikan, $namaRacikan, $marking, $dino, $wulan, $tahun) {
                $service = app(SimbhpStockService::class);
                $prepared = [];
                $requiredByJenis = [];

                foreach ($bahan as $line) {
                    $jenisNama = trim((string) ($line['jenis'] ?? ''));
                    $qty = (int) str_replace(',', '', (string) ($line['qty'] ?? 0));
                    $satuanTransaksi = (string) ($line['satuan_transaksi'] ?? 'besar');
                    if (!in_array($satuanTransaksi, ['besar', 'kecil'], true)) {
                        $satuanTransaksi = 'besar';
                    }
                    if ($jenisNama === '' || $qty <= 0) {
                        throw new \InvalidArgumentException('Bahan dan jumlah wajib diisi');
                    }

                    $jenis = SIMBHPJenis::where('jenis', $jenisNama)->first();
                    if (!$jenis) {
                        throw new \InvalidArgumentException('Bahan '.$jenisNama.' tidak ditemukan');
                    }
                    $setting = $service->getUnitSetting($jenis);
                    if ($satuanTransaksi === 'kecil' && !($setting['has_breakdown'] ?? false)) {
                        throw new \InvalidArgumentException('Satuan kecil tidak tersedia untuk '.$jenisNama);
                    }

                    $qtyBase = $service->calculateBaseQty($jenis, $qty, $satuanTransaksi);
                    $requiredByJenis[$jenisNama] = ($requiredByJenis[$jenisNama] ?? 0) + $qtyBase;
                    $prepared[] = [
                        'jenis' => $jenis,
                        'qty' => $qty,
                        'qty_base' => $qtyBase,
                        'satuan_transaksi' => $satuanTransaksi,
                        'batch_id' => (int) ($line['batch_id'] ?? 0),
                        'expired_baru' => $this->normalizeDateInput($line['expired_baru'] ?? null),
                    ];
                }

                foreach ($requiredByJenis as $jenisNama => $requiredBase) {
                    $stok = $service->getStockBaseByJenis($jenisNama);
                    if ($requiredBase > $stok) {
                        throw new \InvalidArgumentException('Stok bahan '.$jenisNama.' tidak cukup');
                    }
                }

                foreach ($prepared as $line) {
                    $keterangan = $statusRacikan === 'berhasil' ? 'Bahan racikan berhasil' : 'Bahan racikan rusak';
                    if ($line['expired_baru'] && $line['batch_id'] > 0) {
                        $batch = SIMBHPReport::where('id', $line['batch_id'])
                            ->where('jenis', $line['jenis']->jenis)
                            ->whereNotNull('pemasukan')
                            ->where('pemasukan', '>', 0)
                            ->first();
                        if (!$batch) {
                            throw new \InvalidArgumentException('Batch expired untuk '.$line['jenis']->jenis.' tidak ditemukan');
                        }
                        $oldExpired = $batch->masa_expired;
                        $oldKeterangan = trim((string) ($batch->keterangan ?? ''));
                        $expiredNote = 'Expired dibuka racikan '.$marking.': '.($oldExpired ?: '-').' -> '.$line['expired_baru'];
                        $batch->masa_expired = $line['expired_baru'];
                        $batch->keterangan = $oldKeterangan !== '' ? $oldKeterangan.' | '.$expiredNote : $expiredNote;
                        $batch->updated_at = date("Y-m-d H:i:s");
                        $batch->save();
                        $keterangan .= ' | Expired dibuka: '.($oldExpired ?: '-').' -> '.$line['expired_baru'];
                    }

                    SIMBHPReport::create([
                        'tanggal' => $dino,
                        'bulan' => $wulan,
                        'tahun' => $tahun,
                        'deskripsi' => 'Racikan '.$namaRacikan.' - bahan',
                        'pemasukan' => null,
                        'pengeluaran' => $line['qty'],
                        'qty_base' => $line['qty_base'],
                        'satuan_transaksi' => $line['satuan_transaksi'],
                        'jenis' => $line['jenis']->jenis,
                        'keterangan' => $keterangan,
                        'marking' => $marking,
                    ]);
                }

                if ($statusRacikan === 'berhasil') {
                    $hasil = $this->resolveRacikanOutputJenis($request);
                    $qtyHasil = (int) str_replace(',', '', (string) $request->input('hasil_qty', 0));
                    $satuanHasil = (string) $request->input('hasil_satuan_transaksi', 'besar');
                    if (!in_array($satuanHasil, ['besar', 'kecil'], true)) {
                        $satuanHasil = 'besar';
                    }
                    if ($qtyHasil <= 0) {
                        throw new \InvalidArgumentException('Jumlah hasil racikan wajib diisi');
                    }
                    $setting = $service->getUnitSetting($hasil);
                    if ($satuanHasil === 'kecil' && !($setting['has_breakdown'] ?? false)) {
                        throw new \InvalidArgumentException('Satuan kecil tidak tersedia untuk hasil racikan');
                    }
                    $qtyBaseHasil = $service->calculateBaseQty($hasil, $qtyHasil, $satuanHasil);

                    SIMBHPReport::create([
                        'tanggal' => $dino,
                        'bulan' => $wulan,
                        'tahun' => $tahun,
                        'deskripsi' => 'Hasil racikan '.$namaRacikan,
                        'pemasukan' => $qtyHasil,
                        'pengeluaran' => null,
                        'qty_base' => $qtyBaseHasil,
                        'satuan_transaksi' => $satuanHasil,
                        'masa_expired' => $this->normalizeDateInput($request->input('hasil_masa_expired')),
                        'jenis' => $hasil->jenis,
                        'keterangan' => 'Hasil racikan berhasil',
                        'marking' => $marking,
                    ]);
                }
            });
        } catch (\Throwable $e) {
            return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => $e->getMessage()]);
        }

        $message = $statusRacikan === 'berhasil'
            ? 'Racikan berhasil disimpan dan hasil masuk stok'
            : 'Racikan rusak disimpan sebagai pengurangan bahan';

        return response()->json(['status' => 'Success', 'message' => $message]);
    }

    public function exAddbarang(Request $request) {
        $deskripsi = $request->input('set01');
        $pos = $request->input('set02');
        $tanggal = $request->input('set03');
        $jumlah = $request->input('set04');
        $jenis = $request->input('set05');
        $postujuan = $request->input('set06'); // also used as kodejenis when $jenis == 'jenis'
        $alasan = $request->input('set07');
        $set08 = $request->input('set08');
        $set09 = $request->input('set09');
        $set10 = $request->input('set10');
        $satuanTransaksi = $request->input('set11');
        $masaExpired = $this->normalizeDateInput($request->input('set12'));
        if ($tanggal == '' OR is_null($tanggal)){
            $tanggal = date("d-m-Y");
        }
        $total = (int)str_replace(',','',$jumlah);
        if ($jenis == 'jenis'){ $jumlah = '-';}
        if ($deskripsi != ''  and $pos != '' and $tanggal != '' and $jumlah != '' and $jenis != ''){
            if ($jenis == 'jenis'){
                $jenis = $request->input('set02');
                $satuan = $request->input('set03');
                $idne = $request->input('set01');
                $kodeInput = trim((string) $request->input('set06'));
                $satuanKecil = trim((string) $set08);
                $konversiKecil = (int) $set09;
                if ($konversiKecil <= 0){ $konversiKecil = 1; }
                $stokMinimum = (int) $set10;
                if ($stokMinimum < 0){ $stokMinimum = 0; }
                if ($satuanKecil === '') {
                    $konversiKecil = 1;
                } elseif ($konversiKecil <= 1) {
                    return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Jika memakai satuan kecil, konversi harus lebih dari 1']);
                }

                if ($kodeInput !== '') {
                    $kodejenis = strtoupper($kodeInput);
                    $kodejenis = preg_replace('/\s+/', '', $kodejenis);
                    $kodejenis = preg_replace('/[^A-Z0-9._-]/', '', $kodejenis);
                } else {
                    $kodejenis = strtoupper((string) $jenis);
                    $kodejenis = preg_replace('/\s+/', '', $kodejenis);
                    $kodejenis = preg_replace('/[^A-Z0-9._-]/', '', $kodejenis);
                }

                if ($kodejenis === '' || strlen($kodejenis) < 2 || strlen($kodejenis) > 40) {
                    return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Kode barang wajib (2-40 karakter). Gunakan huruf/angka dan simbol - _ .']);
                }

                if ($idne == 'new' OR $idne == ''){
                    $ceksudah = SIMBHPJenis::where('kodejenis', $kodejenis)->where('satuan', $satuan)->count();
                    if ($ceksudah != 0){
                        return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => $jenis.' Sudah Ada, Silahkan Masukkan Jenis Barang Lain']);
                    } else {
                        $input = SIMBHPJenis::create([
                            'kodejenis' => $kodejenis,
                            'jenis' => $jenis,
                            'satuan' => $satuan,
                            'satuan_kecil' => $satuanKecil,
                            'konversi_kecil' => $konversiKecil,
                            'stok_minimum' => $stokMinimum,
                        ]);
                        if ($input){
                            if (Schema::hasColumn('simbhpjenis', 'barcode_besar')) {
                                $service = app(SimbhpStockService::class);
                                $setting = $service->getUnitSetting($input);
                                $input->update([
                                    'barcode_besar' => $service->makeBarcodeValue((int) $input->id, 'besar'),
                                    'barcode_kecil' => ($setting['has_breakdown'] ?? false) ? $service->makeBarcodeValue((int) $input->id, 'kecil') : null,
                                ]);
                            }
                            return response()->json(['status' => 'Success', 'message' => 'Data '.$jenis.' Sukses Ditambahkan']);
                        } else {
                            return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => $jenis.' Gagal di masukkan, silahkan ulangi beberapa saat lagi']);
                        }
                    }
                } else {
                    $ceksudah = SIMBHPJenis::where('id', '!=', $idne)->where('kodejenis', $kodejenis)->where('satuan', $satuan)->count();
                    if ($ceksudah != 0){
                        return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => $jenis.' Sudah Ada, Silahkan Masukkan Jenis Barang Lain']);
                    } else {
                        $input = SIMBHPJenis::where('id', $idne)->update([
                            'kodejenis' => $kodejenis,
                            'jenis' => $jenis,
                            'satuan' => $satuan,
                            'satuan_kecil' => $satuanKecil,
                            'konversi_kecil' => $konversiKecil,
                            'stok_minimum' => $stokMinimum,
                        ]);
                        if ($input){
                            if (Schema::hasColumn('simbhpjenis', 'barcode_besar')) {
                                $row = SIMBHPJenis::find($idne);
                                if ($row) {
                                    $service = app(SimbhpStockService::class);
                                    $setting = $service->getUnitSetting($row);
                                    $row->barcode_besar = $service->makeBarcodeValue((int) $row->id, 'besar');
                                    $row->barcode_kecil = ($setting['has_breakdown'] ?? false) ? $service->makeBarcodeValue((int) $row->id, 'kecil') : null;
                                    $row->save();
                                }
                            }
                            return response()->json(['status' => 'Success', 'message' => 'Data '.$jenis.' Sukses Diupdate']);
                        } else {
                            return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => $jenis.' Gagal di masukkan, silahkan ulangi beberapa saat lagi']);
                        }
                    }
                }
            } else {
                $ahrf = explode("-", $tanggal);
                $tahun = $ahrf[0];
                if(isset($ahrf[1])){ $wulan = (int)$ahrf[1]; } else { $wulan = (int)date("m"); }
                if(isset($ahrf[2])){ $dino = (int)$ahrf[2]; } else { $dino = (int)date("d"); }
                $unitSetting = $this->getJenisUnitSetting($pos);
                if ($jenis == 'pemasukan'){
                    if (($satuanTransaksi ?: 'besar') === 'kecil' && !$unitSetting['has_breakdown']) {
                        return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Barang ini belum memiliki setting pecah satuan']);
                    }
                    $qtyBase = $this->calculateBaseQty($pos, $total, $satuanTransaksi ?: 'besar');
                    $bayar = SIMBHPReport::create([
                        'tanggal' => $dino,
                        'bulan' => $wulan,
                        'tahun' => $tahun,
                        'deskripsi' => $deskripsi,
                        'pemasukan' => $total,
                        'pengeluaran' => null,
                        'qty_base' => $qtyBase,
                        'satuan_transaksi' => $satuanTransaksi ?: 'besar',
                        'masa_expired' => $masaExpired,
                        'jenis' => $pos,
                        'keterangan' => '',
                        'marking' => '',
                    ]);
                } else if ($jenis == 'pengeluaran'){
                    if (($satuanTransaksi ?: 'besar') === 'kecil' && !$unitSetting['has_breakdown']) {
                        return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Pengeluaran satuan kecil tidak tersedia untuk barang ini']);
                    }
                    $getnama = User::where('id', $deskripsi)->first();
                    $nama = $getnama->nama ?? 'Unkown';
                    $deskripsi = 'Diterima oleh '.$nama;
                    $qtyBase = $this->calculateBaseQty($pos, $total, $satuanTransaksi ?: 'besar');
                    $stokSaatIni = $this->getStockBaseByJenis($pos);
                    if ($qtyBase > $stokSaatIni){
                        return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Nominal Melebihi Stok']);
                    } else {
                        $bayar = SIMBHPReport::create([
                            'tanggal' => $dino,
                            'bulan' => $wulan,
                            'tahun' => $tahun,
                            'deskripsi' => $deskripsi,
                            'pemasukan' => null,
                            'pengeluaran' => $total,
                            'qty_base' => $qtyBase,
                            'satuan_transaksi' => $satuanTransaksi ?: 'besar',
                            'jenis' => $pos,
                            'keterangan' => '',
                            'marking' => '',
                        ]);
                    }

                } else if ($jenis == 'opname_pengurangan'){
                    if (($satuanTransaksi ?: 'besar') === 'kecil' && !$unitSetting['has_breakdown']) {
                        return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Pengurangan satuan kecil tidak tersedia untuk barang ini']);
                    }

                    $kategori = strtolower(trim((string) $deskripsi));
                    if (!in_array($kategori, ['rusak', 'hilang'], true)) {
                        return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Kategori opname pengurangan tidak valid']);
                    }

                    $label = ($kategori === 'hilang') ? 'Barang Hilang' : 'Barang Rusak';
                    $deskripsi = 'Stok Opname - ' . $label;
                    $qtyBase = $this->calculateBaseQty($pos, $total, $satuanTransaksi ?: 'besar');
                    $stokSaatIni = $this->getStockBaseByJenis($pos);
                    if ($qtyBase > $stokSaatIni){
                        return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Nominal Melebihi Stok']);
                    } else {
                        $bayar = SIMBHPReport::create([
                            'tanggal' => $dino,
                            'bulan' => $wulan,
                            'tahun' => $tahun,
                            'deskripsi' => $deskripsi,
                            'pemasukan' => null,
                            'pengeluaran' => $total,
                            'qty_base' => $qtyBase,
                            'satuan_transaksi' => $satuanTransaksi ?: 'besar',
                            'jenis' => $pos,
                            'keterangan' => trim((string) $alasan),
                            'marking' => '',
                        ]);
                    }

                } else if ($jenis == 'editor'){
                    if ($alasan == ''){
                        return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Alasan Perubahan Data Wajib Di Isi!!!']);
                    } else {
                        $getdebet = SIMBHPReport::select(DB::raw('SUM(pemasukan) as pemasukan'))->where('jenis', $pos)->groupBy('jenis')->first();
                        if (isset($getdebet->pemasukan)){ $totpemasukan = $getdebet->pemasukan; } else { $totpemasukan = 0 ;}
                        $getkredit = SIMBHPReport::select(DB::raw('SUM(pengeluaran) as pengeluaran'))->where('jenis', $pos)->groupBy('jenis')->first();
                        if (isset($getkredit->pengeluaran)){ $totpepengeluaran = $getkredit->pengeluaran; } else { $totpepengeluaran = 0 ;}
                        $totpepengeluaran = $totpepengeluaran + $total;

                        $rdatalama = SIMBHPReport::where('id', $postujuan)->first();
                        $ldeskripsi = $rdatalama->deskripsi;
                        $lpemasukan = $rdatalama->pemasukan;
                        $lpengeluaran = $rdatalama->pengeluaran;
                        $ljenis = $rdatalama->jenis;
                        $marking = $rdatalama->marking;
                        if ($lpengeluaran == '' OR $lpengeluaran == 0) {
                            $satuanEdit = $rdatalama->satuan_transaksi ?: 'besar';
                            $qtyBaseEdit = $this->calculateBaseQty($pos, $total, $satuanEdit);
                            $ltotal = number_format( $lpemasukan , 0 , '.' , ',' );
                            if ($marking != ''){
                                SIMBHPReport::where('marking', $marking)->whereNotIn('id', [$postujuan])->update([
                                    'tanggal' => $dino,
                                    'bulan' => $wulan,
                                    'tahun' => $tahun,
                                    'pengeluaran' => $total,
                                    'qty_base' => $qtyBaseEdit,
                                    'satuan_transaksi' => $satuanEdit
                                ]);
                            }
                            $bayar = SIMBHPReport::where('id', $postujuan)->update([
                                'tanggal' => $dino,
                                'bulan' => $wulan,
                                'tahun' => $tahun,
                                'deskripsi' => $deskripsi,
                                'jenis' => $pos,
                                'pemasukan' => $total,
                                'qty_base' => $qtyBaseEdit,
                                'satuan_transaksi' => $satuanEdit,
                                'masa_expired' => $masaExpired,
                                'keterangan' => $alasan,
                                'updated_at' => date("Y-m-d H:i:s")
                            ]);
                        } else {
                            $satuanEdit = $rdatalama->satuan_transaksi ?: 'kecil';
                            $qtyBaseEdit = $this->calculateBaseQty($pos, $total, $satuanEdit);
                            $totpepengeluaran = $totpepengeluaran + $total;
                            if ($totpepengeluaran > $totpemasukan){
                                return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Nominal Melebihi Stok']);
                            } else {
                                $ltotal = number_format( $lpengeluaran , 0 , '.' , ',' );
                                if ($marking != ''){
                                    SIMBHPReport::where('marking', $marking)->whereNotIn('id', [$postujuan])->update([
                                        'tanggal' => $dino,
                                        'bulan' => $wulan,
                                        'tahun' => $tahun,
                                        'pemasukan' => $total,
                                        'qty_base' => $qtyBaseEdit,
                                        'satuan_transaksi' => $satuanEdit
                                    ]);
                                }
                                $bayar = SIMBHPReport::where('id', $postujuan)->update([
                                    'tanggal' => $dino,
                                    'bulan' => $wulan,
                                    'tahun' => $tahun,
                                    'deskripsi' => $deskripsi,
                                    'jenis' => $pos,
                                    'pengeluaran' => $total,
                                    'qty_base' => $qtyBaseEdit,
                                    'satuan_transaksi' => $satuanEdit,
                                    'keterangan' => $alasan,
                                    'updated_at' => date("Y-m-d H:i:s")
                                ]);
                            }
                        }
                        $baris1 = '<table class="table table-bordered table-striped"><tr><td colspan=2><p align=center><b>Data Lama</b></p></td><td colspan=2><p align=center><b>Data Perubahan</b></p></td></tr>';
                        $baris2 = '<tr><td>Deskripsi</td><td>'.$ldeskripsi.'</td><td><font color=red>Diubah Menjadi</font></td><td>'.$deskripsi.'</td></tr>';
                        $baris3 = '<tr><td>Jenis</td><td>'.$ljenis.'</td><td><font color=red>Diubah Menjadi</font></td><td>'.$pos.'</td></tr>';
                        $baris4 = '<tr><td>Total</td><td>'.$ltotal.'</td><td><font color=red>Diubah Menjadi</font></td><td>'.$jumlah.'</td></tr>';
                        $baris5 = '<tr><td><b>Dengan Alasan</b></td><td colspan=3>'.$alasan.'</td</tr>';
                        $baris6 = '<tr><td><b>Eksekutor</b></td><td colspan=3>'.Session('nama').'</td</tr></table>';
                        $perubahan = $baris1.$baris2.$baris3.$baris4.$baris5.$baris6;
                        Xfiles::create([
                            'xmarking' => 'SIMBHP-'.$postujuan.'-'.time(),
                            'xtabel' => 'History SIMBHP',
                            'xjenis' => '',
                            'xfile' => $perubahan
                        ]);
                    }
                } else {
                    if ($alasan == ''){
                        return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Alasan Perubahan Data Wajib Di Isi!!!']);
                    }
                    else {
                        $rdatalama = SIMBHPReport::where('id', $postujuan)->first();
                        $ldeskripsi = $rdatalama->deskripsi;
                        $lpemasukan = $rdatalama->pemasukan;
                        $lpengeluaran = $rdatalama->pengeluaran;
                        $ljenis = $rdatalama->jenis;
                        $marking = $rdatalama->marking;
                        if ($lpengeluaran == '' or $lpengeluaran == 0) {
                            $ltotal = number_format( $lpemasukan , 0 , '.' , ',' );
                        } else {
                            $ltotal = number_format( $lpengeluaran , 0 , '.' , ',' );
                        }
                        $baris1 = '<table class="table table-bordered table-striped"><tr><td colspan=2><p align=center><b>Data Lama</b></p></td><td colspan=2><p align=center><b>Data Perubahan</b></p></td></tr>';
                        $baris2 = '<tr><td>Deskripsi</td><td>'.$ldeskripsi.'</td><td colspan=2><font color=red>DIHAPUS</font></td></tr>';
                        $baris3 = '<tr><td>Jenis</td><td>'.$ljenis.'</td><td colspan=2><font color=red>DIHAPUS</font></td></tr>';
                        $baris4 = '<tr><td>Total</td><td>'.$ltotal.'</td><td colspan=2><font color=red>DIHAPUS</font></td></tr>';
                        $baris5 = '<tr><td><b>Dengan Alasan</b></td><td colspan=3>'.$alasan.'</td</tr>';
                        $baris6 = '<tr><td><b>Eksekutor</b></td><td colspan=3>'.Session('nama').'</td</tr></table>';
                        $perubahan = $baris1.$baris2.$baris3.$baris4.$baris5.$baris6;

                        Xfiles::create([
                            'xmarking' => 'SIMBHP-'.$postujuan.'-'.time(),
                            'xtabel' => 'History SIMBHP',
                            'xjenis' => '',
                            'xfile' => $perubahan
                        ]);
                        if ($marking != ''){
                            $bayar = SIMBHPReport::where('marking', $marking)->delete();
                        } else {
                            $bayar = SIMBHPReport::where('id', $postujuan)->delete();
                        }
                    }
                }
                if ($bayar){
                    return response()->json(['status' => 'Success', 'message' => 'Transaksi '.$jenis.' Sukses Dilaksanakan']);
                } else {
                    return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Update Gagal, Pastikan Data Yang anda Isi Sudah Sesuai']);
                }
            }
        }
        else {
            return response()->json(['icon' => 'error', 'warna' => '#bf441d', 'status' => 'Gagal', 'message' => 'Pastikan Formnya Anda Isi dengan Lengkap']);
        }
    }

    public function exKwitansi(Request $request)
    {
        return response()->json([
            'status' => 'Info',
            'message' => 'Fitur kwitansi gudang belum diaktifkan pada versi ini.'
        ]);
    }

    private function calculateBaseQty($jenisNama, $qtyInput, $satuanTransaksi = 'besar')
    {
        $qtyInput = (int) $qtyInput;
        if ($qtyInput < 0) { $qtyInput = 0; }
        $setting = $this->getJenisUnitSetting($jenisNama);
        $konversi = (int) $setting['konversi'];
        if ($konversi <= 0) { $konversi = 1; }

        if ($satuanTransaksi === 'kecil') {
            return $qtyInput;
        }

        return $qtyInput * $konversi;
    }

    private function getJenisUnitSetting($jenisNama)
    {
        $jenis = SIMBHPJenis::where('jenis', $jenisNama)->first();
        $satuanKecil = trim((string) ($jenis->satuan_kecil ?? ''));
        $konversi = (int) ($jenis->konversi_kecil ?? 1);
        if ($konversi <= 0) { $konversi = 1; }
        $hasBreakdown = ($satuanKecil !== '' && $konversi > 1);

        return [
            'has_breakdown' => $hasBreakdown,
            'konversi' => $konversi,
            'satuan_kecil' => $satuanKecil,
        ];
    }

    private function getStockBaseByJenis($jenisNama)
    {
        $rows = SIMBHPReport::where('jenis', $jenisNama)->get();
        $masuk = 0;
        $keluar = 0;
        foreach ($rows as $row) {
            if (!is_null($row->pemasukan) && (int)$row->pemasukan > 0) {
                $masuk += $this->extractBaseQty($row, 'pemasukan');
            }
            if (!is_null($row->pengeluaran) && (int)$row->pengeluaran > 0) {
                $keluar += $this->extractBaseQty($row, 'pengeluaran');
            }
        }

        return $masuk - $keluar;
    }

    private function extractBaseQty($row, $direction = 'pemasukan')
    {
        if (!is_null($row->qty_base) && (int)$row->qty_base > 0) {
            return (int) $row->qty_base;
        }

        if ($direction === 'pengeluaran') {
            return (int) ($row->pengeluaran ?? 0);
        }

        return (int) ($row->pemasukan ?? 0);
    }

    private function formatStockDisplay($saldoBase, $satuanBesar, $satuanKecil, $konversi)
    {
        $saldoBase = (int) $saldoBase;
        $konversi = (int) $konversi;
        if ($konversi <= 1 || $satuanKecil == '') {
            return number_format($saldoBase, 0, '.', ',').' '.$satuanBesar;
        }

        $besar = intdiv($saldoBase, $konversi);
        $kecil = $saldoBase % $konversi;

        return number_format($besar, 0, '.', ',').' '.$satuanBesar.' + '.number_format($kecil, 0, '.', ',').' '.$satuanKecil;
    }

    private function getLowStockWarnings()
    {
        $warnings = [];
        $allJenis = SIMBHPJenis::orderBy('jenis', 'ASC')->get();
        foreach ($allJenis as $item) {
            $saldoBase = $this->getStockBaseByJenis($item->jenis);
            $minimum = (int) ($item->stok_minimum ?? 0);
            if ($saldoBase <= $minimum) {
                $warnings[] = [
                    'jenis' => $item->jenis,
                    'saldo' => $this->formatStockDisplay(
                        $saldoBase,
                        $item->satuan,
                        $item->satuan_kecil ?? '',
                        $item->konversi_kecil ?? 1
                    ),
                    'minimum' => $minimum,
                    'satuan_kecil' => $item->satuan_kecil ?: $item->satuan,
                ];
            }
        }

        return $warnings;
    }

    private function getExpiringSoonItems()
    {
        $today = date('Y-m-d');
        $limitDate = date('Y-m-d', strtotime('+30 days'));

        $rows = SIMBHPReport::whereNotNull('masa_expired')
            ->whereNotNull('pemasukan')
            ->where('pemasukan', '>', 0)
            ->where('masa_expired', '>=', $today)
            ->where('masa_expired', '<=', $limitDate)
            ->orderBy('masa_expired', 'ASC')
            ->orderBy('id', 'DESC')
            ->get();

        $items = [];
        foreach ($rows as $row) {
            $expiredAt = (string) $row->masa_expired;
            $daysLeft = (int) floor((strtotime($expiredAt) - strtotime($today)) / 86400);
            if ($daysLeft < 0) {
                continue;
            }

            $badge = 'warning';
            if ($daysLeft <= 7) {
                $badge = 'danger';
            } elseif ($daysLeft <= 14) {
                $badge = 'warning';
            }

            $items[] = [
                'jenis' => $row->jenis ?: '-',
                'deskripsi' => $row->deskripsi ?: '-',
                'qty' => (int) ($row->pemasukan ?? 0),
                'satuan' => $row->satuan_transaksi ?: 'besar',
                'tanggal_masuk' => sprintf('%02d-%02d-%04d', (int) $row->tanggal, (int) $row->bulan, (int) $row->tahun),
                'masa_expired' => date('d-m-Y', strtotime($expiredAt)),
                'sisa_hari' => $daysLeft,
                'badge' => $badge,
            ];
        }

        return $items;
    }

    private function getAggregateBaseQty($direction, $scope = 'harian')
    {
        $now = date('Y-m-d');
        $d = (int) date('d', strtotime($now));
        $m = (int) date('m', strtotime($now));
        $y = (int) date('Y', strtotime($now));

        $query = SIMBHPReport::query();
        if ($scope === 'harian') {
            $query->where('tanggal', $d)->where('bulan', $m)->where('tahun', $y);
        } elseif ($scope === 'bulanan') {
            $query->where('bulan', $m)->where('tahun', $y);
        } else {
            $query->where('tahun', $y);
        }

        $rows = $query->get();
        $total = 0;
        foreach ($rows as $row) {
            if ($direction === 'pemasukan' && !is_null($row->pemasukan) && (int)$row->pemasukan > 0) {
                $total += $this->extractBaseQty($row, 'pemasukan');
            }
            if ($direction === 'pengeluaran' && !is_null($row->pengeluaran) && (int)$row->pengeluaran > 0) {
                $total += $this->extractBaseQty($row, 'pengeluaran');
            }
        }

        return $total;
    }

    private function getUsagePerJenis($scope = 'harian')
    {
        $now = date('Y-m-d');
        $d = (int) date('d', strtotime($now));
        $m = (int) date('m', strtotime($now));
        $y = (int) date('Y', strtotime($now));

        $query = SIMBHPReport::query();
        if ($scope === 'harian') {
            $query->where('tanggal', $d)->where('bulan', $m)->where('tahun', $y);
        } elseif ($scope === 'bulanan') {
            $query->where('bulan', $m)->where('tahun', $y);
        } else {
            $query->where('tahun', $y);
        }

        $rows = $query->get();
        $grouped = [];
        $jenisNames = [];
        foreach ($rows as $row) {
            $jenis = $row->jenis ?: 'Tidak Diketahui';
            if (!isset($grouped[$jenis])) {
                $grouped[$jenis] = ['jenis' => $jenis, 'masuk' => 0, 'keluar' => 0, 'sisa' => 0];
                $jenisNames[] = $jenis;
            }
            if (!is_null($row->pemasukan) && (int)$row->pemasukan > 0) {
                $grouped[$jenis]['masuk'] += $this->extractBaseQty($row, 'pemasukan');
            }
            if (!is_null($row->pengeluaran) && (int)$row->pengeluaran > 0) {
                $grouped[$jenis]['keluar'] += $this->extractBaseQty($row, 'pengeluaran');
            }
        }

        if (!empty($jenisNames)) {
            $stockMap = app(SimbhpStockService::class)->getStockBaseByJenisMany($jenisNames);
            foreach ($grouped as $jenis => &$item) {
                $item['sisa'] = (int) ($stockMap[$jenis] ?? 0);
            }
            unset($item);
        }

        return array_values($grouped);
    }

    private function normalizeDateInput($value)
    {
        $value = trim((string) $value);
        if ($value === '') {
            return null;
        }

        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
            return $value;
        }

        if (preg_match('/^\d{2}-\d{2}-\d{4}$/', $value)) {
            $parts = explode('-', $value);
            return $parts[2].'-'.$parts[1].'-'.$parts[0];
        }

        return null;
    }

    private function resolveRacikanOutputJenis(Request $request): SIMBHPJenis
    {
        $mode = (string) $request->input('hasil_mode', 'existing');
        if ($mode === 'new') {
            $nama = trim((string) $request->input('hasil_new_jenis'));
            $satuan = trim((string) $request->input('hasil_new_satuan'));
            $kode = trim((string) $request->input('hasil_new_kode'));
            $satuanKecil = trim((string) $request->input('hasil_new_satuan_kecil'));
            $konversi = (int) $request->input('hasil_new_konversi', 1);
            $stokMinimum = (int) $request->input('hasil_new_stok_minimum', 0);

            if ($nama === '' || $satuan === '') {
                throw new \InvalidArgumentException('Nama dan satuan hasil racikan baru wajib diisi');
            }
            if ($konversi <= 0) {
                $konversi = 1;
            }
            if ($stokMinimum < 0) {
                $stokMinimum = 0;
            }
            if ($satuanKecil !== '' && $konversi <= 1) {
                throw new \InvalidArgumentException('Konversi hasil racikan baru harus lebih dari 1 jika memakai satuan kecil');
            }

            if ($kode !== '') {
                $kode = strtoupper($kode);
                $kode = preg_replace('/\s+/', '', $kode);
                $kode = preg_replace('/[^A-Z0-9._-]/', '', $kode);
            } else {
                $kode = strtoupper($nama);
                $kode = preg_replace('/\s+/', '', $kode);
                $kode = preg_replace('/[^A-Z0-9._-]/', '', $kode);
            }
            if ($kode === '' || strlen($kode) < 2 || strlen($kode) > 40) {
                throw new \InvalidArgumentException('Kode hasil racikan wajib 2-40 karakter');
            }

            $existing = SIMBHPJenis::where('kodejenis', $kode)->first();
            if ($existing) {
                return $existing;
            }

            $jenis = SIMBHPJenis::create([
                'kodejenis' => $kode,
                'jenis' => $nama,
                'satuan' => $satuan,
                'satuan_kecil' => $satuanKecil,
                'konversi_kecil' => $konversi,
                'stok_minimum' => $stokMinimum,
            ]);

            if (Schema::hasColumn('simbhpjenis', 'barcode_besar')) {
                $service = app(SimbhpStockService::class);
                $setting = $service->getUnitSetting($jenis);
                $jenis->update([
                    'barcode_besar' => $service->makeBarcodeValue((int) $jenis->id, 'besar'),
                    'barcode_kecil' => ($setting['has_breakdown'] ?? false) ? $service->makeBarcodeValue((int) $jenis->id, 'kecil') : null,
                ]);
            }

            return $jenis;
        }

        $jenisNama = trim((string) $request->input('hasil_jenis'));
        if ($jenisNama === '') {
            throw new \InvalidArgumentException('Barang hasil racikan wajib dipilih');
        }

        $jenis = SIMBHPJenis::where('jenis', $jenisNama)->first();
        if (!$jenis) {
            throw new \InvalidArgumentException('Barang hasil racikan tidak ditemukan');
        }

        return $jenis;
    }

    private function getJenisRows()
    {
        $rows = [];
        $items = SIMBHPJenis::orderBy('jenis', 'ASC')->get();
        foreach ($items as $item) {
            $saldoBase = $this->getStockBaseByJenis($item->jenis);
            $konversi = (int) ($item->konversi_kecil ?? 1);
            if ($konversi <= 0) { $konversi = 1; }
            $rows[] = [
                'id' => $item->id,
                'kodejenis' => $item->kodejenis ?? '',
                'jenis' => $item->jenis,
                'satuan' => $item->satuan,
                'satuan_kecil' => $item->satuan_kecil ?? '',
                'konversi_kecil' => $konversi,
                'stok_minimum' => (int) ($item->stok_minimum ?? 0),
                'saldo' => $this->formatStockDisplay($saldoBase, $item->satuan, $item->satuan_kecil ?? '', $konversi),
                'warning' => ($saldoBase <= (int) ($item->stok_minimum ?? 0)) ? 'YA' : 'TIDAK',
            ];
        }

        return $rows;
    }
}
