<?php

namespace App\Controllers\Admin;

use App\Controllers\BaseController;
use App\Models\BagIssueModel;
use App\Models\BagIssueItemCodeModel;
use App\Models\BagInventoryModel;
use App\Models\CodeKindModel;
use App\Models\CodeDetailModel;
use App\Models\FreeRecipientModel;
use App\Models\PackagingUnitModel;

class BagIssue extends BaseController
{
    private BagIssueModel $issueModel;
    private BagIssueItemCodeModel $issueItemCodeModel;

    public function __construct()
    {
        $this->issueModel = model(BagIssueModel::class);
        $this->issueItemCodeModel = model(BagIssueItemCodeModel::class);
    }

    /**
     * 낱장 수량을 품목코드 단위로 분해한다.
     *
     * @return array<int,array{issueCode:string,qty:int}>
     */
    private function buildIssueCodeRows(int $bi2Idx, int $sheetQty, array $packUnit): array
    {
        $sheetQty = max(0, $sheetQty);
        if ($sheetQty <= 0) {
            return [];
        }

        $chunkSize = max(
            1,
            (int) ($packUnit['totalPerBox'] ?? 0),
            (int) ($packUnit['packPerSheet'] ?? 0)
        );

        $rows = [];
        $remaining = $sheetQty;
        $seq = 1;
        while ($remaining > 0) {
            $qty = min($chunkSize, $remaining);
            $rows[] = [
                'issueCode' => sprintf('%d-%06d-%03d', (int) date('y'), $bi2Idx, $seq),
                'qty' => $qty,
            ];
            $remaining -= $qty;
            $seq++;
        }

        return $rows;
    }

    public function index()
    {
        helper('admin');
        $lgIdx = admin_effective_lg_idx();
        if (! $lgIdx) {
            return redirect()->to(work_area_home_url())->with('error', '지자체를 선택해 주세요.');
        }

        $builder = $this->issueModel->where('bi2_lg_idx', $lgIdx);
        $startDate = $this->request->getGet('start_date');
        $endDate   = $this->request->getGet('end_date');
        if ($startDate) {
            $builder->where('bi2_issue_date >=', $startDate);
        }
        if ($endDate) {
            $builder->where('bi2_issue_date <=', $endDate);
        }

        $list  = $builder->orderBy('bi2_issue_date', 'DESC')->orderBy('bi2_idx', 'DESC')->paginate(20);
        $pager = $this->issueModel->pager;

        return $this->renderWorkPage('무료용 불출 관리', 'admin/bag_issue/index', compact('list', 'startDate', 'endDate', 'pager'));
    }

    public function create()
    {
        helper('admin');
        $lgIdx    = admin_effective_lg_idx();
        $kind     = model(CodeKindModel::class)->where('ck_code', 'O')->first();
        $bagCodes = $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $lgIdx) : [];

        return $this->renderWorkPage('무료용 불출 처리', 'admin/bag_issue/create', compact('bagCodes'));
    }

    public function store()
    {
        helper('admin');
        $lgIdx = admin_effective_lg_idx();

        $rules = [
            'bi2_year'       => 'required|is_natural_no_zero',
            'bi2_quarter'    => 'required|in_list[1,2,3,4]',
            'bi2_issue_type' => 'required|max_length[20]',
            'bi2_issue_date' => 'required|valid_date[Y-m-d]',
            'bi2_dest_name'  => 'required|max_length[100]',
            // 사이트 다건 입력(item_bag_code/item_qty)과 기존 관리자 단건 입력을 함께 허용
            'bi2_bag_code'   => 'permit_empty|max_length[50]',
            'bi2_qty'        => 'permit_empty|is_natural_no_zero',
        ];
        if (! $this->validate($rules)) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $kindO  = model(CodeKindModel::class)->where('ck_code', 'O')->first();
        $issueType = trim((string) $this->request->getPost('bi2_issue_type'));
        $destType = trim((string) ($this->request->getPost('bi2_dest_type') ?? ''));
        $destName = trim((string) ($this->request->getPost('bi2_dest_name') ?? ''));
        $destDongCode = trim((string) ($this->request->getPost('bi2_dest_dong_code') ?? ''));

        if ($destType === '') {
            $destType = '동사무소';
        }
        if ($issueType === '공공용' && $destType === '동사무소') {
            $destType = '구청';
        }

        if ($issueType === '무료용' && $destDongCode !== '') {
            $existsFreeDong = model(FreeRecipientModel::class)
                ->where('fr_lg_idx', $lgIdx)
                ->where('fr_state', 1)
                ->where('fr_dong_code', $destDongCode)
                ->first();
            if (! $existsFreeDong) {
                return redirect()->back()->withInput()->with('error', '선택한 불출처는 무료용 대상이 아닙니다.');
            }
        }

        $invRows = model(BagInventoryModel::class)
            ->where('bi_lg_idx', $lgIdx)
            ->where('bi_qty >', 0)
            ->findAll();
        $inventoryMap = [];
        foreach ($invRows as $inv) {
            $code = (string) ($inv->bi_bag_code ?? '');
            if ($code === '') {
                continue;
            }
            $inventoryMap[$code] = [
                'qty' => (int) ($inv->bi_qty ?? 0),
                'name' => (string) ($inv->bi_bag_name ?? ''),
            ];
        }

        $unitRows = model(PackagingUnitModel::class)
            ->where('pu_lg_idx', $lgIdx)
            ->where('pu_state', 1)
            ->findAll();
        $packMap = [];
        foreach ($unitRows as $unit) {
            $code = (string) ($unit->pu_bag_code ?? '');
            if ($code === '') {
                continue;
            }
            $packMap[$code] = [
                'packPerSheet' => max(1, (int) ($unit->pu_pack_per_sheet ?? 1)),
                'totalPerBox' => max(1, (int) ($unit->pu_total_per_box ?? 1)),
            ];
        }

        $items = [];
        $itemCodes = $this->request->getPost('item_bag_code');
        $itemQtys = $this->request->getPost('item_qty');
        $itemPacks = $this->request->getPost('item_pack');
        $itemCodes = is_array($itemCodes) ? $itemCodes : [];
        $itemQtys = is_array($itemQtys) ? $itemQtys : [];
        $itemPacks = is_array($itemPacks) ? $itemPacks : [];

        $count = max(count($itemCodes), count($itemQtys), count($itemPacks));
        for ($i = 0; $i < $count; $i++) {
            $bagCode = trim((string) ($itemCodes[$i] ?? ''));
            $qtyRaw = (int) ($itemQtys[$i] ?? 0);
            $pack = trim((string) ($itemPacks[$i] ?? 'sheet'));
            if ($bagCode === '' || $qtyRaw <= 0) {
                continue;
            }
            if (! in_array($pack, ['box', 'pack', 'sheet'], true)) {
                $pack = 'sheet';
            }
            $packUnit = $packMap[$bagCode] ?? ['packPerSheet' => 1, 'totalPerBox' => 1];
            $sheetQty = $qtyRaw;
            if ($pack === 'box') {
                $sheetQty = $qtyRaw * (int) $packUnit['totalPerBox'];
            } elseif ($pack === 'pack') {
                $sheetQty = $qtyRaw * (int) $packUnit['packPerSheet'];
            }
            $sheetQty = max(1, (int) $sheetQty);

            $detail = $kindO
                ? model(CodeDetailModel::class)->findResolvedByKindAndCode((int) $kindO->ck_idx, $bagCode, $lgIdx)
                : null;
            $bagName = $detail ? (string) ($detail->cd_name ?? '') : (string) ($inventoryMap[$bagCode]['name'] ?? '');
            if ($bagName === '') {
                $bagName = (string) $bagCode;
            }

            $items[] = [
                'bagCode' => $bagCode,
                'bagName' => $bagName,
                'pack' => $pack,
                'rawQty' => $qtyRaw,
                'sheetQty' => $sheetQty,
            ];
        }

        // 기존 관리자 단건 폼과의 호환
        if ($items === []) {
            $singleBagCode = trim((string) $this->request->getPost('bi2_bag_code'));
            $singleQty = (int) $this->request->getPost('bi2_qty');
            if ($singleBagCode !== '' && $singleQty > 0) {
                $detail = $kindO
                    ? model(CodeDetailModel::class)->findResolvedByKindAndCode((int) $kindO->ck_idx, $singleBagCode, $lgIdx)
                    : null;
                $bagName = $detail ? (string) ($detail->cd_name ?? '') : (string) ($inventoryMap[$singleBagCode]['name'] ?? '');
                if ($bagName === '') {
                    $bagName = (string) $singleBagCode;
                }
                $items[] = [
                    'bagCode' => $singleBagCode,
                    'bagName' => $bagName,
                    'pack' => 'sheet',
                    'rawQty' => $singleQty,
                    'sheetQty' => $singleQty,
                ];
            }
        }

        if ($items === []) {
            return redirect()->back()->withInput()->with('error', '불출 품목을 1건 이상 입력해 주세요.');
        }

        $requiredByBag = [];
        foreach ($items as $item) {
            $code = (string) $item['bagCode'];
            if (! isset($requiredByBag[$code])) {
                $requiredByBag[$code] = 0;
            }
            $requiredByBag[$code] += (int) $item['sheetQty'];
        }
        foreach ($requiredByBag as $code => $requiredQty) {
            $available = (int) ($inventoryMap[$code]['qty'] ?? 0);
            if ($available <= 0) {
                return redirect()->back()->withInput()->with('error', '입고 재고가 없는 봉투코드는 불출할 수 없습니다: ' . $code);
            }
            if ($available < $requiredQty) {
                return redirect()->back()->withInput()->with('error', '재고가 부족합니다: ' . $code . ' (재고 ' . number_format($available) . ', 요청 ' . number_format($requiredQty) . ')');
            }
        }

        $db = \Config\Database::connect();
        $db->transStart();
        $hasIssueCodeTable = $db->tableExists('bag_issue_item_code');

        $issueYear = (int) $this->request->getPost('bi2_year');
        $issueQuarter = (int) $this->request->getPost('bi2_quarter');
        $issueDate = (string) $this->request->getPost('bi2_issue_date');
        $createdCount = 0;
        helper('audit');
        foreach ($items as $item) {
            $issueData = [
                'bi2_lg_idx'     => $lgIdx,
                'bi2_year'       => $issueYear,
                'bi2_quarter'    => $issueQuarter,
                'bi2_issue_type' => $issueType,
                'bi2_issue_date' => $issueDate,
                'bi2_dest_type'  => $destType,
                'bi2_dest_name'  => $destName,
                'bi2_bag_code'   => (string) $item['bagCode'],
                'bi2_bag_name'   => (string) $item['bagName'],
                'bi2_qty'        => (int) $item['sheetQty'],
                'bi2_status'     => 'normal',
                'bi2_regdate'    => date('Y-m-d H:i:s'),
            ];
            $this->issueModel->insert($issueData);
            $bi2Idx = (int) $this->issueModel->getInsertID();
            audit_log('create', 'bag_issue', $bi2Idx, null, array_merge($issueData, ['bi2_idx' => $bi2Idx]));

            if ($hasIssueCodeTable) {
                $codeRows = $this->buildIssueCodeRows($bi2Idx, (int) $item['sheetQty'], $packMap[(string) $item['bagCode']] ?? []);
                foreach ($codeRows as $codeRow) {
                    $this->issueItemCodeModel->insert([
                        'bic_lg_idx' => $lgIdx,
                        'bic_bi2_idx' => $bi2Idx,
                        'bic_bag_code' => (string) $item['bagCode'],
                        'bic_issue_code' => (string) $codeRow['issueCode'],
                        'bic_qty' => (int) $codeRow['qty'],
                        'bic_cancel_qty' => 0,
                        'bic_state' => 'normal',
                        'bic_regdate' => date('Y-m-d H:i:s'),
                    ]);
                }
            }

            model(BagInventoryModel::class)->adjustQty(
                $lgIdx,
                (string) $item['bagCode'],
                (string) $item['bagName'],
                -((int) $item['sheetQty'])
            );
            $createdCount++;
        }

        $db->transComplete();

        if (! $db->transStatus()) {
            return redirect()->back()->withInput()->with('error', '불출 처리 중 오류가 발생했습니다.');
        }

        return redirect()->to(mgmt_url('bag-issues'))->with('success', $createdCount . '건 불출 처리되었습니다.');
    }

    public function cancel(int $id)
    {
        helper('admin');
        $item = $this->issueModel->find($id);
        if (! $item || (int) $item->bi2_lg_idx !== admin_effective_lg_idx()) {
            return redirect()->to(mgmt_url('bag-issues'))->with('error', '불출 내역을 찾을 수 없습니다.');
        }

        $db = \Config\Database::connect();
        $db->transStart();
        $hasIssueCodeTable = $db->tableExists('bag_issue_item_code');

        $before = (array) $item;
        $this->issueModel->update($id, ['bi2_status' => 'cancelled']);
        helper('audit');
        audit_log('update', 'bag_issue', $id, $before, ['bi2_status' => 'cancelled']);

        $restoreQty = (int) $item->bi2_qty;
        if ($hasIssueCodeTable) {
            $codeRows = $db->table('bag_issue_item_code')
                ->select('bic_idx, bic_qty, bic_cancel_qty')
                ->where('bic_lg_idx', (int) $item->bi2_lg_idx)
                ->where('bic_bi2_idx', $id)
                ->get()
                ->getResultArray();
            $restoreQty = 0;
            foreach ($codeRows as $codeRow) {
                $bicIdx = (int) ($codeRow['bic_idx'] ?? 0);
                $qty = (int) ($codeRow['bic_qty'] ?? 0);
                $oldCancel = (int) ($codeRow['bic_cancel_qty'] ?? 0);
                $restoreQty += max(0, $qty - $oldCancel);
                $db->table('bag_issue_item_code')
                    ->where('bic_idx', $bicIdx)
                    ->update([
                        'bic_cancel_qty' => $qty,
                        'bic_state' => 'cancelled',
                    ]);
            }
        }

        model(BagInventoryModel::class)->adjustQty((int) $item->bi2_lg_idx, $item->bi2_bag_code, $item->bi2_bag_name, $restoreQty);
        $this->issueModel->update($id, ['bi2_qty' => 0, 'bi2_status' => 'cancelled']);

        $db->transComplete();

        return redirect()->to(mgmt_url('bag-issues'))->with('success', '불출이 취소되었습니다.');
    }
}
