<?php

namespace App\Services;

use App\SIMBHPJenis;
use Illuminate\Support\Facades\DB;

class SimbhpStockService
{
    /**
     * Barcode format:
     * - MAIN (satuan besar): 91{ID(8-digit)}1  (example: 91000001231)
     * - BREAKDOWN (satuan kecil): 91{ID(8-digit)}2 (example: 91000001232)
     *
     * Backward compatibility:
     * - {KODE}-B / {KODE}-K
     * - plain {KODE} treated as "besar"
     *
     * @return array{kode:string, jenis_id:int|null, satuan_transaksi:string, barcode_value:string}
     */
    public function parseBarcode(string $raw): array
    {
        $raw = $this->sanitizeKode($raw);
        if ($raw === '') {
            return [
                'kode' => '',
                'jenis_id' => null,
                'satuan_transaksi' => 'besar',
                'barcode_value' => '',
            ];
        }

        if (preg_match('/^91(\d{8})([12])$/', $raw, $m)) {
            $id = (int) ltrim((string) $m[1], '0');
            $unitDigit = (string) $m[2];

            return [
                'kode' => '',
                'jenis_id' => $id > 0 ? $id : null,
                'satuan_transaksi' => ($unitDigit === '2') ? 'kecil' : 'besar',
                'barcode_value' => $raw,
            ];
        }

        if (preg_match('/^(.*)-(B|K)$/', $raw, $m)) {
            $base = $this->sanitizeKode((string) $m[1]);
            $suffix = (string) $m[2];
            return [
                'kode' => $base,
                'jenis_id' => null,
                'satuan_transaksi' => ($suffix === 'K') ? 'kecil' : 'besar',
                'barcode_value' => $base . '-' . $suffix,
            ];
        }

        return [
            'kode' => $raw,
            'jenis_id' => null,
            'satuan_transaksi' => 'besar',
            'barcode_value' => $raw,
        ];
    }

    public function sanitizeKode(string $kode): string
    {
        $kode = strtoupper(trim($kode));
        $kode = preg_replace('/\s+/', '', $kode);
        $kode = preg_replace('/[^A-Z0-9._-]/', '', $kode);

        return substr($kode, 0, 40);
    }

    public function getJenisByKode(string $kode): ?SIMBHPJenis
    {
        $parsed = $this->parseBarcode($kode);
        $jenisId = $parsed['jenis_id'] ?? null;
        if (!is_null($jenisId) && (int) $jenisId > 0) {
            return SIMBHPJenis::where('id', (int) $jenisId)->first();
        }

        $kode = $parsed['kode'] ?? '';
        if ($kode === '') {
            return null;
        }

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

    public function makeBarcodeValue(int $jenisId, string $satuanTransaksi = 'besar'): string
    {
        if ($jenisId <= 0) {
            return '';
        }

        $idPart = str_pad((string) $jenisId, 8, '0', STR_PAD_LEFT);
        $unitDigit = ($satuanTransaksi === 'kecil') ? '2' : '1';

        return '91' . $idPart . $unitDigit;
    }

    /**
     * @return array{has_breakdown: bool, konversi: int, satuan_kecil: string}
     */
    public function getUnitSetting(SIMBHPJenis $jenis): array
    {
        $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,
        ];
    }

    public function calculateBaseQty(SIMBHPJenis $jenis, int $qtyInput, string $satuanTransaksi = 'besar'): int
    {
        $qtyInput = (int) $qtyInput;
        if ($qtyInput < 0) {
            $qtyInput = 0;
        }

        $setting = $this->getUnitSetting($jenis);
        $konversi = (int) ($setting['konversi'] ?? 1);
        if ($konversi <= 0) {
            $konversi = 1;
        }

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

        return $qtyInput * $konversi;
    }

    public function getStockBaseByJenis(string $jenisNama): int
    {
        $jenisNama = trim((string) $jenisNama);
        if ($jenisNama === '') {
            return 0;
        }

        $row = DB::table('simbhpreport')
            ->where('jenis', $jenisNama)
            ->selectRaw("SUM(CASE WHEN pemasukan IS NOT NULL AND pemasukan > 0 THEN COALESCE(qty_base, pemasukan) ELSE 0 END) AS masuk_base")
            ->selectRaw("SUM(CASE WHEN pengeluaran IS NOT NULL AND pengeluaran > 0 THEN COALESCE(qty_base, pengeluaran) ELSE 0 END) AS keluar_base")
            ->first();

        $masuk = (int) ($row->masuk_base ?? 0);
        $keluar = (int) ($row->keluar_base ?? 0);

        return $masuk - $keluar;
    }

    /**
     * @param  array<int, string>  $jenisNames
     * @return array<string, int> map jenis => saldo_base
     */
    public function getStockBaseByJenisMany(array $jenisNames): array
    {
        $jenisNames = array_values(array_filter(array_map(function ($v) {
            return trim((string) $v);
        }, $jenisNames)));

        if (count($jenisNames) === 0) {
            return [];
        }

        $rows = DB::table('simbhpreport')
            ->whereIn('jenis', $jenisNames)
            ->select('jenis')
            ->selectRaw("SUM(CASE WHEN pemasukan IS NOT NULL AND pemasukan > 0 THEN COALESCE(qty_base, pemasukan) ELSE 0 END) AS masuk_base")
            ->selectRaw("SUM(CASE WHEN pengeluaran IS NOT NULL AND pengeluaran > 0 THEN COALESCE(qty_base, pengeluaran) ELSE 0 END) AS keluar_base")
            ->groupBy('jenis')
            ->get();

        $map = [];
        foreach ($rows as $row) {
            $jenis = (string) ($row->jenis ?? '');
            if ($jenis === '') {
                continue;
            }
            $map[$jenis] = ((int) ($row->masuk_base ?? 0)) - ((int) ($row->keluar_base ?? 0));
        }

        foreach ($jenisNames as $jenis) {
            if (!array_key_exists($jenis, $map)) {
                $map[$jenis] = 0;
            }
        }

        return $map;
    }

    public function formatStockDisplay(int $saldoBase, string $satuanBesar, string $satuanKecil, int $konversi): string
    {
        $saldoBase = (int) $saldoBase;
        $konversi = (int) $konversi;
        if ($konversi <= 1 || trim($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;
    }
}
