<?php

declare(strict_types=1);

namespace App\Libraries;

/**
 * 봉투번호확인(번호알기) — 코드 입력 → 바코드·인쇄숫자·인식번호 분해.
 * LOT-입고PK-팩/박스-낱장 형식 및 DB 등록 바코드 지원.
 */
class BagNumberLookup
{
    /**
     * @return array{
     *   ok: bool,
     *   message: string,
     *   input: string,
     *   barcode_text: string,
     *   print_text: string,
     *   recognition_text: string,
     *   unit: string,
     *   bag_code: string,
     *   bag_name: string
     * }
     */
    public function resolve(string $raw, ?int $lgIdx = null): array
    {
        $input = trim($raw);
        if ($input === '') {
            return $this->fail('코드를 입력해 주세요.', $input);
        }

        $normalized = strtoupper(preg_replace('/\s+/', '', $input) ?? $input);
        $parsed     = $this->parseStructuredCode($normalized);

        if ($parsed === null && $lgIdx !== null) {
            $parsed = $this->resolveFromDatabase($lgIdx, $normalized);
        }

        if ($parsed === null) {
            return $this->fail('인식할 수 없는 코드입니다. 봉투 바코드(LOT-입고번호-팩/박스-낱장) 형식을 확인해 주세요.', $input);
        }

        return [
            'ok'               => true,
            'message'          => '',
            'input'            => $input,
            'barcode_text'     => $this->formatRow($parsed['barcode'], 4),
            'print_text'       => $this->formatRow($parsed['print'], 3),
            'recognition_text' => $this->formatRow($parsed['recognition'], 2),
            'unit'             => (string) ($parsed['unit'] ?? ''),
            'bag_code'         => (string) ($parsed['bag_code'] ?? ''),
            'bag_name'         => (string) ($parsed['bag_name'] ?? ''),
        ];
    }

    /**
     * @param list<string> $parts
     */
    private function formatRow(array $parts, int $slots): string
    {
        $cells = array_pad($parts, $slots, '-');
        foreach ($cells as $i => $cell) {
            if ($cell === '' || $cell === null) {
                $cells[$i] = '-';
            }
        }

        return implode('  ', $cells);
    }

    /**
     * @return array{barcode:list<string>,print:list<string>,recognition:list<string>,unit:string,bag_code?:string,bag_name?:string}|null
     */
    private function parseStructuredCode(string $code): ?array
    {
        if (preg_match('/^([A-Z0-9]+)-(\d{6})-P(\d{3})-S(\d+)$/i', $code, $m) === 1) {
            $sheet = str_pad((string) $m[4], 5, '0', STR_PAD_LEFT);

            return [
                'barcode'      => [(string) $m[1], (string) $m[2], 'P' . $m[3], 'S' . $sheet],
                'print'        => [(string) (int) $m[2], (string) (int) $m[3], (string) (int) $sheet],
                'recognition'  => [(string) $m[2], 'P' . $m[3]],
                'unit'         => '낱장',
            ];
        }

        if (preg_match('/^([A-Z0-9]+)-(\d{6})-P(\d{3})$/i', $code, $m) === 1) {
            return [
                'barcode'     => [(string) $m[1], (string) $m[2], 'P' . $m[3], '-'],
                'print'       => [(string) (int) $m[2], (string) (int) $m[3], '-'],
                'recognition' => [(string) $m[2], 'P' . $m[3]],
                'unit'        => '팩',
            ];
        }

        if (preg_match('/^([A-Z0-9]+)-(\d{6})-B(\d{3})$/i', $code, $m) === 1) {
            return [
                'barcode'     => [(string) $m[1], (string) $m[2], 'B' . $m[3], '-'],
                'print'       => [(string) (int) $m[2], (string) (int) $m[3], '-'],
                'recognition' => [(string) $m[2], 'B' . $m[3]],
                'unit'        => '박스',
            ];
        }

        if (preg_match('/^([A-Z0-9]{4,8})$/i', $code) === 1) {
            return [
                'barcode'     => [$code, '-', '-', '-'],
                'print'       => ['-', '-', '-'],
                'recognition' => [$code, '-'],
                'unit'        => 'LOT',
            ];
        }

        return null;
    }

    /**
     * @return array{barcode:list<string>,print:list<string>,recognition:list<string>,unit:string,bag_code?:string,bag_name?:string}|null
     */
    private function resolveFromDatabase(int $lgIdx, string $code): ?array
    {
        $db = \Config\Database::connect();
        if (! $db->tableExists('bag_receiving_pack_code')) {
            return null;
        }

        $row = $db->table('bag_receiving_pack_code')
            ->where('brpc_lg_idx', $lgIdx)
            ->where('brpc_pack_code', $code)
            ->get()
            ->getRowArray();
        if (is_array($row) && $row !== []) {
            return $this->parsedFromPackRow($code, $row, '팩');
        }

        $boxRows = $db->table('bag_receiving_pack_code')
            ->where('brpc_lg_idx', $lgIdx)
            ->where('brpc_box_code', $code)
            ->limit(1)
            ->get()
            ->getRowArray();
        if (is_array($boxRows) && $boxRows !== []) {
            return $this->parsedFromPackRow($code, $boxRows, '박스');
        }

        $sheetRow = $db->table('bag_receiving_pack_code')
            ->where('brpc_lg_idx', $lgIdx)
            ->where('brpc_sheet_start_code <=', $code)
            ->where('brpc_sheet_end_code >=', $code)
            ->limit(1)
            ->get()
            ->getRowArray();
        if (is_array($sheetRow) && $sheetRow !== []) {
            $parsed = $this->parseStructuredCode($code);
            if ($parsed !== null) {
                $parsed['bag_code'] = (string) ($sheetRow['brpc_bag_code'] ?? '');
                $parsed['bag_name'] = (string) ($sheetRow['brpc_bag_name'] ?? '');

                return $parsed;
            }
        }

        $exactSheet = $db->table('bag_receiving_pack_code')
            ->where('brpc_lg_idx', $lgIdx)
            ->groupStart()
            ->where('brpc_sheet_start_code', $code)
            ->orWhere('brpc_sheet_end_code', $code)
            ->groupEnd()
            ->limit(1)
            ->get()
            ->getRowArray();
        if (is_array($exactSheet) && $exactSheet !== []) {
            $parsed = $this->parseStructuredCode($code);
            if ($parsed !== null) {
                $parsed['bag_code'] = (string) ($exactSheet['brpc_bag_code'] ?? '');
                $parsed['bag_name'] = (string) ($exactSheet['brpc_bag_name'] ?? '');

                return $parsed;
            }
        }

        return null;
    }

    /**
     * @param array<string, mixed> $row
     * @return array{barcode:list<string>,print:list<string>,recognition:list<string>,unit:string,bag_code:string,bag_name:string}
     */
    private function parsedFromPackRow(string $code, array $row, string $unit): array
    {
        $parsed = $this->parseStructuredCode($code);
        if ($parsed === null) {
            $parsed = [
                'barcode'     => [$code, '-', '-', '-'],
                'print'       => ['-', '-', '-'],
                'recognition' => ['-', '-'],
                'unit'        => $unit,
            ];
        }
        $parsed['unit']     = $unit;
        $parsed['bag_code'] = (string) ($row['brpc_bag_code'] ?? '');
        $parsed['bag_name'] = (string) ($row['brpc_bag_name'] ?? '');

        return $parsed;
    }

    /**
     * @return array{ok:bool,message:string,input:string,barcode_text:string,print_text:string,recognition_text:string,unit:string,bag_code:string,bag_name:string}
     */
    private function fail(string $message, string $input): array
    {
        return [
            'ok'               => false,
            'message'          => $message,
            'input'            => $input,
            'barcode_text'     => $this->formatRow([], 4),
            'print_text'       => $this->formatRow([], 3),
            'recognition_text' => $this->formatRow([], 2),
            'unit'             => '',
            'bag_code'         => '',
            'bag_name'         => '',
        ];
    }
}
