<?php

namespace App\Services\Production;

use App\DTOs\Production\DynamicBomEntry;
use App\Models\Orders\Order;
use App\Models\Process;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;

/**
 * 수주 → 생산지시 시 절곡 공정용 bending_info JSON 자동 생성
 *
 * 입력: Order (rootNodes eager loaded) + processId
 * 출력: ['bending_info' => array, 'context' => array] — bending_info + dynamic_bom 생성 컨텍스트
 *
 * @see react/src/components/production/WorkOrders/documents/bending/types.ts
 */
class BendingInfoBuilder
{
    // 표준 원자재 길이 버킷 (5130 레거시 write_form.php 기준)
    private const GUIDE_RAIL_LENGTHS = [2438, 3000, 3500, 4000, 4300];

    private const SHUTTER_BOX_LENGTHS = [1219, 2438, 3000, 3500, 4000, 4150];

    /**
     * 수주의 노드/BOM 데이터로 bending_info JSON 생성
     */
    /**
     * @param  array|null  $nodeIds  특정 노드만 집계 (null이면 전체)
     */
    public function build(Order $order, int $processId, ?array $nodeIds = null): ?array
    {
        // 1. 절곡 공정인지 확인
        $process = Process::find($processId);
        if (! $process || $process->process_name !== '절곡') {
            return null;
        }

        // 2. 루트 노드 확인 (nodeIds가 있으면 해당 노드만 필터)
        $nodes = $order->rootNodes;
        if ($nodeIds !== null) {
            $nodes = $nodes->whereIn('id', $nodeIds);
        }
        if ($nodes->isEmpty()) {
            return null;
        }

        // 3. 첫 번째 루트 노드에서 product_code 파싱
        $firstNode = $nodes->first();
        $productCode = $firstNode->options['product_code'] ?? '';
        if (empty($productCode)) {
            return null;
        }

        $productInfo = $this->parseProductCode($productCode);
        if (empty($productInfo['productCode'])) {
            return null;
        }

        // 4. 재질 매핑
        $materials = $this->getMaterialMapping(
            $productInfo['productCode'],
            $productInfo['finishMaterial']
        );

        // 5. 노드 집계 (치수별 그룹핑 + BOM 카테고리 분류)
        $aggregated = $this->aggregateNodes($nodes);

        // 6. bending_info 조립
        $bendingInfo = $this->assembleBendingInfo($productInfo, $materials, $aggregated);

        // 7. 셔터박스 크기 추출 (dynamic_bom 컨텍스트용)
        $caseBom = $aggregated['bomCategories']['shutterBox_case'] ?? null;
        $motorBom = $aggregated['bomCategories']['motor'] ?? null;
        $boxSize = null;
        if ($caseBom) {
            $motorCapacity = $this->extractMotorCapacity($motorBom);
            $boxSize = $motorCapacity ? $this->getShutterBoxSize($motorCapacity) : null;
            if (! $boxSize) {
                $boxSize = str_replace('BD-케이스-', '', $caseBom['item_code'] ?? '');
            }
        }

        return [
            'bending_info' => $bendingInfo,
            'context' => [
                'productCode' => $productInfo['productCode'],
                'guideType' => $productInfo['guideType'],
                'finishMaterial' => $productInfo['finishMaterial'],
                'materials' => $materials,
                'boxSize' => $boxSize,
                'hasSmokeRail' => isset($aggregated['bomCategories']['smokeBarrier_rail']),
                'hasSmokeCase' => isset($aggregated['bomCategories']['smokeBarrier_case']),
            ],
        ];
    }

    /**
     * 개소(work_order_item) 단위 dynamic_bom 생성
     *
     * @param  array  $context  build() 반환값의 'context'
     * @param  int  $width  개소의 오픈폭 (mm)
     * @param  int  $height  개소의 오픈높이 (mm)
     * @param  int  $qty  개소 수량
     * @param  int  $tenantId  테넌트 ID (item 조회용)
     * @return array DynamicBomEntry::toArray() 배열
     */
    public function buildDynamicBomForItem(array $context, int $width, int $height, int $qty, int $tenantId = 287): array
    {
        $resolver = new PrefixResolver;
        $entries = [];

        $productCode = $context['productCode'];
        $guideType = $context['guideType'];
        $finishMaterial = $context['finishMaterial'];
        $materials = $context['materials'];
        $boxSize = $context['boxSize'];
        $hasExtraFinish = ! empty($materials['guideRailExtraFinish']);

        // ─── 1. 가이드레일 세부품목 ───
        $dimGroups = [['height' => $height, 'width' => $width, 'qty' => $qty]];
        $heightData = $this->heightLengthData($dimGroups);

        // 가이드레일은 개구부 양쪽 2개이므로 수량 ×2
        foreach ($heightData as &$entry) {
            $entry['quantity'] *= 2;
        }
        unset($entry);

        $guideTypes = match ($guideType) {
            '혼합형' => ['wall', 'side'],
            '측면형' => ['side'],
            default => ['wall'],
        };

        $guidePartTypes = ['finish', 'body', 'c_type', 'd_type'];
        if ($hasExtraFinish) {
            $guidePartTypes[] = 'extra_finish';
        }
        $guidePartTypes[] = 'base';

        foreach ($guideTypes as $gType) {
            foreach ($heightData as $ld) {
                foreach ($guidePartTypes as $partType) {
                    $prefix = $resolver->resolveGuideRailPrefix($partType, $gType, $productCode);
                    if (empty($prefix)) {
                        continue;
                    }

                    $itemCode = $resolver->buildItemCode($prefix, $ld['length']);
                    if (! $itemCode) {
                        Log::warning('BendingInfoBuilder: lengthCode 변환 실패', ['prefix' => $prefix, 'length' => $ld['length']]);

                        continue;
                    }

                    $itemId = $resolver->resolveItemId($itemCode, $tenantId);
                    if (! $itemId) {
                        Log::warning('BendingInfoBuilder: 미등록 품목', ['code' => $itemCode]);

                        continue;
                    }

                    $entries[] = new DynamicBomEntry(
                        child_item_id: $itemId,
                        child_item_code: $itemCode,
                        lot_prefix: $prefix,
                        part_type: PrefixResolver::partTypeName($partType),
                        category: 'guideRail',
                        material_type: $this->resolvePartMaterial($partType, $gType, $materials),
                        length_mm: $ld['length'],
                        qty: $ld['quantity'],
                    );
                }
            }
        }

        // ─── 2. 하단마감재 세부품목 ───
        [$qty3000, $qty4000] = $this->bottomBarDistribution($width);
        $bottomLengths = array_filter([3000 => $qty3000 * $qty, 4000 => $qty4000 * $qty]);

        $bottomPartTypes = ['main', 'lbar', 'reinforce'];
        $hasBottomExtra = ! empty($materials['bottomBarExtraFinish']) && $materials['bottomBarExtraFinish'] !== '없음';
        if ($hasBottomExtra) {
            $bottomPartTypes[] = 'extra';
        }

        foreach ($bottomLengths as $length => $lengthQty) {
            if ($lengthQty <= 0) {
                continue;
            }
            foreach ($bottomPartTypes as $partType) {
                $prefix = $resolver->resolveBottomBarPrefix($partType, $productCode, $finishMaterial);
                $itemCode = $resolver->buildItemCode($prefix, $length);
                if (! $itemCode) {
                    continue;
                }
                $itemId = $resolver->resolveItemId($itemCode, $tenantId);
                if (! $itemId) {
                    Log::warning('BendingInfoBuilder: 미등록 하단마감재', ['code' => $itemCode]);

                    continue;
                }

                $entries[] = new DynamicBomEntry(
                    child_item_id: $itemId,
                    child_item_code: $itemCode,
                    lot_prefix: $prefix,
                    part_type: PrefixResolver::partTypeName($partType),
                    category: 'bottomBar',
                    material_type: $materials['bottomBarFinish'],
                    length_mm: $length,
                    qty: $lengthQty,
                );
            }
        }

        // ─── 3. 셔터박스 세부품목 ───
        if ($boxSize) {
            $dist = $this->shutterBoxDistribution($width);
            // 상부덮개(top_cover), 마구리(fin_cover)는 1219mm 기준으로 별도 생성 (아래 256행~)
            $shutterPartTypes = ['front', 'lintel', 'inspection', 'rear_corner'];

            // 작업일지와 동일한 순서: 파트 → 길이
            foreach ($shutterPartTypes as $partType) {
                foreach ($dist as $length => $count) {
                    $totalCount = $count * $qty;
                    if ($totalCount <= 0) {
                        continue;
                    }

                    $prefix = $resolver->resolveShutterBoxPrefix($partType);
                    $itemCode = $resolver->buildItemCode($prefix, $length);
                    if (! $itemCode) {
                        continue;
                    }
                    $itemId = $resolver->resolveItemId($itemCode, $tenantId);
                    if (! $itemId) {
                        Log::warning('BendingInfoBuilder: 미등록 셔터박스', ['code' => $itemCode]);

                        continue;
                    }

                    $entries[] = new DynamicBomEntry(
                        child_item_id: $itemId,
                        child_item_code: $itemCode,
                        lot_prefix: $prefix,
                        part_type: PrefixResolver::partTypeName($partType),
                        category: 'shutterBox',
                        material_type: 'EGI',
                        length_mm: $length,
                        qty: $totalCount,
                    );
                }
            }

            // 상부덮개 수량: ceil(width / 1219) × qty (1219mm 단위)
            $coverQty = (int) ceil($width / 1219) * $qty;
            if ($coverQty > 0) {
                $coverPrefix = $resolver->resolveShutterBoxPrefix('top_cover');
                $coverCode = $resolver->buildItemCode($coverPrefix, 1219);
                if ($coverCode) {
                    $coverId = $resolver->resolveItemId($coverCode, $tenantId);
                    if ($coverId) {
                        $entries[] = new DynamicBomEntry(
                            child_item_id: $coverId,
                            child_item_code: $coverCode,
                            lot_prefix: $coverPrefix,
                            part_type: PrefixResolver::partTypeName('top_cover'),
                            category: 'shutterBox',
                            material_type: 'EGI',
                            length_mm: 1219,
                            qty: $coverQty,
                        );
                    }
                }
            }

            // 마구리 수량: qty × 2
            $finQty = $qty * 2;
            if ($finQty > 0) {
                $finPrefix = $resolver->resolveShutterBoxPrefix('fin_cover');
                // 마구리는 박스 높이 기준이므로 셔터박스 최소 단위 사용
                $finCode = $resolver->buildItemCode($finPrefix, 1219);
                if ($finCode) {
                    $finId = $resolver->resolveItemId($finCode, $tenantId);
                    if ($finId) {
                        $entries[] = new DynamicBomEntry(
                            child_item_id: $finId,
                            child_item_code: $finCode,
                            lot_prefix: $finPrefix,
                            part_type: PrefixResolver::partTypeName('fin_cover'),
                            category: 'shutterBox',
                            material_type: 'EGI',
                            length_mm: 1219,
                            qty: $finQty,
                        );
                    }
                }
            }
        }

        // ─── 4. 연기차단재 세부품목 ───
        if ($context['hasSmokeRail'] || $context['hasSmokeCase']) {
            $smokePrefix = $resolver->resolveSmokeBarrierPrefix();

            // W50 (레일용): open_height + 250 → 표준 길이
            if ($context['hasSmokeRail']) {
                $col24 = $height + 250;
                $w50Length = $this->bucketToStandardLength($col24, [2438, 3000, 3500, 4000, 4300]);
                if ($w50Length && $col24 <= 4300) {
                    $w50Code = $resolver->buildItemCode($smokePrefix, $w50Length, 'w50');
                    if ($w50Code) {
                        $w50Id = $resolver->resolveItemId($w50Code, $tenantId);
                        if ($w50Id) {
                            $entries[] = new DynamicBomEntry(
                                child_item_id: $w50Id,
                                child_item_code: $w50Code,
                                lot_prefix: $smokePrefix,
                                part_type: '연기차단재(W50)',
                                category: 'smokeBarrier',
                                material_type: 'GI',
                                length_mm: $w50Length,
                                qty: 2 * $qty,
                            );
                        }
                    }
                }
            }

            // W80 (케이스용): floor((width+240)*2/3000 + 1) × qty
            if ($context['hasSmokeCase']) {
                $col38 = $width + 240;
                $w80PerNode = (int) floor(($col38 * 2 / 3000) + 1);
                $w80Qty = $w80PerNode * $qty;
                if ($w80Qty > 0) {
                    // W80은 3000mm 기본 (레거시 동일)
                    $w80Code = $resolver->buildItemCode($smokePrefix, 3000, 'w80');
                    if ($w80Code) {
                        $w80Id = $resolver->resolveItemId($w80Code, $tenantId);
                        if ($w80Id) {
                            $entries[] = new DynamicBomEntry(
                                child_item_id: $w80Id,
                                child_item_code: $w80Code,
                                lot_prefix: $smokePrefix,
                                part_type: '연기차단재(W80)',
                                category: 'smokeBarrier',
                                material_type: 'GI',
                                length_mm: 3000,
                                qty: $w80Qty,
                            );
                        }
                    }
                }
            }
        }

        return DynamicBomEntry::toArrayList($entries);
    }

    /**
     * 파트타입 + 가이드타입 → 실제 재질 결정
     */
    private function resolvePartMaterial(string $partType, string $guideType, array $materials): string
    {
        return match ($partType) {
            'finish' => $materials['guideRailFinish'],
            'extra_finish' => $materials['guideRailExtraFinish'],
            'body', 'c_type', 'd_type' => $materials['bodyMaterial'],
            'base' => 'EGI',
            default => '',
        };
    }

    /**
     * product_code 파싱
     * "FG-KSS02-벽면형-SUS" → productCode, guideType, finishMaterial
     */
    private function parseProductCode(string $fullCode): array
    {
        $parts = explode('-', $fullCode);

        // FG 접두사 제거
        if (($parts[0] ?? '') === 'FG') {
            array_shift($parts);
        }

        $finish = $parts[2] ?? 'EGI';

        return [
            'productCode' => $parts[0] ?? '',
            'guideType' => $parts[1] ?? '벽면형',
            'finishMaterial' => $finish === 'SUS' ? 'SUS마감' : 'EGI마감',
        ];
    }

    /**
     * BOM 아이템 카테고리 분류 (item_code/item_name 패턴 매칭)
     */
    private function categorizeBomItem(array $bomItem): ?string
    {
        $code = $bomItem['item_code'] ?? '';
        $name = $bomItem['item_name'] ?? '';

        if (str_starts_with($code, 'BD-가이드레일')) {
            return 'guideRail';
        }
        if (str_starts_with($code, 'BD-케이스')) {
            return 'shutterBox_case';
        }
        if (str_starts_with($code, 'BD-마구리')) {
            return 'shutterBox_finCover';
        }
        if (str_contains($name, '하장바')) {
            return 'bottomBar';
        }
        if ($code === 'EST-SMOKE-레일용') {
            return 'smokeBarrier_rail';
        }
        if ($code === 'EST-SMOKE-케이스용') {
            return 'smokeBarrier_case';
        }
        if (str_starts_with($code, 'BD-L-BAR')) {
            return 'detail_lbar';
        }
        if (str_starts_with($code, 'BD-보강평철')) {
            return 'detail_reinforce';
        }
        if (str_starts_with($code, 'EST-MOTOR-')) {
            return 'motor';
        }

        return null;
    }

    /**
     * 노드 집계: 치수별 그룹핑 + BOM 카테고리 분류
     */
    private function aggregateNodes(Collection $nodes): array
    {
        $dimensionGroups = [];
        $totalNodeQty = 0;
        $bomCategories = [];

        foreach ($nodes as $node) {
            $opts = $node->options ?? [];
            // 절곡 원자재 길이는 오픈 사이즈 기준 (제조치수는 +offset 포함으로 부적합)
            $width = (int) ($opts['open_width'] ?? $opts['width'] ?? 0);
            $height = (int) ($opts['open_height'] ?? $opts['height'] ?? 0);
            $nodeQty = $node->quantity ?? 1;
            $totalNodeQty += $nodeQty;

            // 치수별 그룹핑
            $dimKey = "{$height}_{$width}";
            if (! isset($dimensionGroups[$dimKey])) {
                $dimensionGroups[$dimKey] = [
                    'height' => (int) $height,
                    'width' => (int) $width,
                    'qty' => 0,
                ];
            }
            $dimensionGroups[$dimKey]['qty'] += $nodeQty;

            // BOM 아이템 카테고리 분류 (첫 노드에서 메타데이터 추출)
            $bomItems = $opts['bom_result']['items'] ?? [];
            foreach ($bomItems as $bom) {
                $cat = $this->categorizeBomItem($bom);
                if ($cat && ! isset($bomCategories[$cat])) {
                    $bomCategories[$cat] = $bom;
                }
            }
        }

        return [
            'dimensionGroups' => array_values($dimensionGroups),
            'totalNodeQty' => $totalNodeQty,
            'bomCategories' => $bomCategories,
        ];
    }

    /**
     * bending_info JSON 조립 (BendingInfoExtended 구조)
     */
    private function assembleBendingInfo(array $productInfo, array $materials, array $agg): array
    {
        $guideRailBom = $agg['bomCategories']['guideRail'] ?? null;
        $caseBom = $agg['bomCategories']['shutterBox_case'] ?? null;
        $finCoverBom = $agg['bomCategories']['shutterBox_finCover'] ?? null;
        $lbarBom = $agg['bomCategories']['detail_lbar'] ?? null;
        $motorBom = $agg['bomCategories']['motor'] ?? null;
        $dimGroups = $agg['dimensionGroups'];

        // 가이드레일 baseSize 추출: BD-가이드레일-KSS01-SUS-120*70 → "120*70"
        $baseSize = $guideRailBom ? $this->extractBaseSize($guideRailBom['item_code'] ?? '') : '';

        return [
            'productCode' => $productInfo['productCode'],
            'finishMaterial' => $productInfo['finishMaterial'],
            'common' => $this->buildCommon($productInfo['guideType'], $baseSize, $dimGroups),
            'detailParts' => $this->buildDetailParts($guideRailBom, $lbarBom, $materials),
            'guideRail' => $this->buildGuideRail($productInfo['guideType'], $baseSize, $materials, $dimGroups, $productInfo['productCode']),
            'bottomBar' => $this->buildBottomBar($materials, $dimGroups),
            'shutterBox' => $this->buildShutterBox($caseBom, $finCoverBom, $motorBom, $baseSize, $dimGroups),
            'smokeBarrier' => $this->buildSmokeBarrier($dimGroups),
        ];
    }

    // ─────────────────────────────────────────────────
    // common 섹션
    // ─────────────────────────────────────────────────

    private function buildCommon(string $guideType, string $baseSize, array $dimGroups): array
    {
        return [
            'kind' => $guideType.' '.str_replace('*', 'X', $baseSize),
            'type' => $guideType.'('.$baseSize.')',
            'lengthQuantities' => $this->heightLengthQuantities($dimGroups),
        ];
    }

    // ─────────────────────────────────────────────────
    // detailParts 섹션
    // ─────────────────────────────────────────────────

    private function buildDetailParts(?array $guideRailBom, ?array $lbarBom, array $materials): array
    {
        $parts = [];

        if ($guideRailBom) {
            $baseSize = $this->extractBaseSize($guideRailBom['item_code'] ?? '');
            $parts[] = [
                'partName' => '가이드레일',
                'material' => $materials['guideRailFinish'],
                'barcyInfo' => $baseSize,
            ];
        }

        if ($lbarBom) {
            // BD-L-BAR-KSS01-17*60 → "17*60"
            $lbarSize = $this->extractLastSize($lbarBom['item_code'] ?? '');
            $parts[] = [
                'partName' => 'L-BAR',
                'material' => $this->extractFinishFromCode($lbarBom['item_code'] ?? '', $materials),
                'barcyInfo' => $lbarSize,
            ];
        }

        return $parts;
    }

    // ─────────────────────────────────────────────────
    // guideRail 섹션
    // ─────────────────────────────────────────────────

    private function buildGuideRail(string $guideType, string $baseSize, array $materials, array $dimGroups, string $productCode): array
    {
        $heightData = $this->heightLengthData($dimGroups);

        // 가이드레일은 개구부 양쪽 2개이므로 수량 ×2
        foreach ($heightData as &$entry) {
            $entry['quantity'] *= 2;
        }
        unset($entry);

        $baseDims = $this->getBaseDimensions($productCode);

        if ($guideType === '혼합형') {
            // 혼합형: 벽면+측면 둘 다
            return [
                'wall' => [
                    'baseSize' => $baseSize,
                    'baseDimension' => $baseDims['wall'],
                    'lengthData' => $heightData,
                ],
                'side' => [
                    'baseSize' => $baseSize,
                    'baseDimension' => $baseDims['side'],
                    'lengthData' => $heightData,
                ],
            ];
        }

        if ($guideType === '측면형') {
            return [
                'wall' => null,
                'side' => [
                    'baseSize' => $baseSize,
                    'baseDimension' => $baseDims['side'],
                    'lengthData' => $heightData,
                ],
            ];
        }

        // 벽면형 (기본)
        return [
            'wall' => [
                'baseSize' => $baseSize,
                'baseDimension' => $baseDims['wall'],
                'lengthData' => $heightData,
            ],
            'side' => null,
        ];
    }

    // ─────────────────────────────────────────────────
    // bottomBar 섹션
    // ─────────────────────────────────────────────────

    private function buildBottomBar(array $materials, array $dimGroups): array
    {
        $length3000Qty = 0;
        $length4000Qty = 0;

        // 레거시 Slat_updateCol49to51() 동일 로직: 오픈폭 범위별 3000/4000 조합 배분
        foreach ($dimGroups as $group) {
            $width = $group['width'];
            $qty = $group['qty'];

            [$qty3000, $qty4000] = $this->bottomBarDistribution($width);
            $length3000Qty += $qty3000 * $qty;
            $length4000Qty += $qty4000 * $qty;
        }

        return [
            'material' => $materials['bottomBarFinish'],
            'extraFinish' => $materials['bottomBarExtraFinish'],
            'length3000Qty' => $length3000Qty,
            'length4000Qty' => $length4000Qty,
        ];
    }

    // ─────────────────────────────────────────────────
    // shutterBox 섹션
    // ─────────────────────────────────────────────────

    private function buildShutterBox(?array $caseBom, ?array $finCoverBom, ?array $motorBom, string $guideBaseSize, array $dimGroups): array
    {
        if (! $caseBom) {
            return [];
        }

        // 셔터박스 크기: 모터용량 → 브라켓 → 박스 매핑 (레거시 Slat_updateCol37)
        $motorCapacity = $this->extractMotorCapacity($motorBom);
        $boxSize = $motorCapacity ? $this->getShutterBoxSize($motorCapacity) : null;

        if (! $boxSize) {
            // fallback: BOM 케이스 item_code에서 추출 (BD-케이스-500*380 → "500*380")
            $boxSize = str_replace('BD-케이스-', '', $caseBom['item_code'] ?? '');
        }

        $boxParts = explode('*', $boxSize);
        $boxWidth = (int) ($boxParts[0] ?? 0);
        $boxHeight = (int) ($boxParts[1] ?? 0);

        // railWidth: 가이드레일 baseSize에서 첫 번째 숫자 (120*70 → 120)
        $guideParts = explode('*', $guideBaseSize);
        $railWidth = (int) ($guideParts[0] ?? 0);

        // 상부덮개 수량: ceil(openWidth / 1219) × nodeQty (레거시 Slat_updateCol45)
        $coverQty = 0;
        // 마구리 수량: nodeQty × 2 (레거시 Slat_updateCol46: col15 * 2)
        $finCoverQty = 0;
        foreach ($dimGroups as $group) {
            $coverQty += (int) ceil($group['width'] / 1219) * $group['qty'];
            $finCoverQty += $group['qty'] * 2;
        }

        // lengthData: 표준 원자재 조합 배분 (레거시 Slat_updateCol39to43)
        $widthData = $this->shutterBoxCombinedLengthData($dimGroups);

        return [
            [
                'size' => $boxSize,
                'direction' => '양면',
                'railWidth' => $railWidth,
                'frontBottom' => $boxHeight,
                'coverQty' => $coverQty,
                'finCoverQty' => $finCoverQty,
                'lengthData' => $widthData,
            ],
        ];
    }

    /**
     * dimGroups를 조합 배분하여 셔터박스 lengthData 생성
     */
    private function shutterBoxCombinedLengthData(array $dimGroups): array
    {
        $combined = [];

        foreach ($dimGroups as $group) {
            $dist = $this->shutterBoxDistribution($group['width']);
            foreach ($dist as $length => $count) {
                if ($count > 0) {
                    $combined[$length] = ($combined[$length] ?? 0) + $count * $group['qty'];
                }
            }
        }

        $result = [];
        ksort($combined);
        foreach ($combined as $length => $qty) {
            $result[] = ['length' => $length, 'quantity' => $qty];
        }

        return $result;
    }

    /**
     * 오픈폭에 따른 셔터박스 표준 원자재 조합 배분
     * 레거시 5130/estimate/common/common_addrowJS.php Slat_updateCol39to43() 동일
     *
     * @return array<int,int> [1219 => qty, 2438 => qty, 3000 => qty, 3500 => qty, 4000 => qty, 4150 => qty]
     */
    private function shutterBoxDistribution(int $openWidth): array
    {
        $d = [1219 => 0, 2438 => 0, 3000 => 0, 3500 => 0, 4000 => 0, 4150 => 0];

        // col39 (1219mm)
        if ($openWidth <= 1219) {
            $d[1219] = 1;
        } elseif ($openWidth > 4150 && $openWidth <= 4219) {
            $d[1219] = 1;
        } elseif ($openWidth > 4219 && $openWidth <= 4719) {
            $d[1219] = 1;
        } elseif ($openWidth > 4876 && $openWidth <= 5219) {
            $d[1219] = 1;
        } elseif ($openWidth > 5219 && $openWidth <= 5369) {
            $d[1219] = 1;
        } elseif ($openWidth > 9026 && $openWidth <= 9219) {
            $d[1219] = 1;
        }

        // col40 (2438mm)
        if ($openWidth > 1219 && $openWidth <= 2438) {
            $d[2438] = 1;
        } elseif ($openWidth > 4719 && $openWidth <= 4876) {
            $d[2438] = 2;
        } elseif ($openWidth > 5369 && $openWidth <= 5938) {
            $d[2438] = 1;
        } elseif ($openWidth > 6000 && $openWidth <= 6438) {
            $d[2438] = 1;
        } elseif ($openWidth > 6500 && $openWidth <= 6588) {
            $d[2438] = 1;
        } elseif ($openWidth > 8300 && $openWidth <= 8376) {
            $d[2438] = 2;
        } elseif ($openWidth > 8376 && $openWidth <= 8438) {
            $d[2438] = 1;
        } elseif ($openWidth > 8438 && $openWidth <= 8876) {
            $d[2438] = 2;
        } elseif ($openWidth > 9000 && $openWidth <= 9026) {
            $d[2438] = 2;
        } elseif ($openWidth > 9219 && $openWidth <= 9438) {
            $d[2438] = 1;
        } elseif ($openWidth > 10150 && $openWidth <= 10738) {
            $d[2438] = 1;
        }

        // col41 (3000mm)
        if ($openWidth > 2438 && $openWidth <= 3000) {
            $d[3000] = 1;
        } elseif ($openWidth > 4150 && $openWidth <= 4219) {
            $d[3000] = 1;
        } elseif ($openWidth > 5369 && $openWidth <= 5438) {
            $d[3000] = 1;
        } elseif ($openWidth > 5938 && $openWidth <= 6000) {
            $d[3000] = 2;
        } elseif ($openWidth > 6438 && $openWidth <= 6500) {
            $d[3000] = 1;
        } elseif ($openWidth > 7000 && $openWidth <= 7150) {
            $d[3000] = 1;
        } elseif ($openWidth > 8376 && $openWidth <= 8438) {
            $d[3000] = 2;
        } elseif ($openWidth > 8876 && $openWidth <= 9000) {
            $d[3000] = 3;
        } elseif ($openWidth > 9438 && $openWidth <= 10150) {
            $d[3000] = 2;
        } elseif ($openWidth > 10738 && $openWidth <= 11000) {
            $d[3000] = 1;
        }

        // col42 (3500mm)
        if ($openWidth > 3000 && $openWidth <= 3500) {
            $d[3500] = 1;
        } elseif ($openWidth > 4219 && $openWidth <= 4719) {
            $d[3500] = 1;
        } elseif ($openWidth > 5438 && $openWidth <= 5938) {
            $d[3500] = 1;
        } elseif ($openWidth > 6438 && $openWidth <= 6500) {
            $d[3500] = 1;
        } elseif ($openWidth > 6588 && $openWidth <= 7000) {
            $d[3500] = 2;
        } elseif ($openWidth > 7150 && $openWidth <= 7650) {
            $d[3500] = 1;
        } elseif ($openWidth > 8300 && $openWidth <= 8376) {
            $d[3500] = 1;
        } elseif ($openWidth > 9219 && $openWidth <= 9438) {
            $d[3500] = 2;
        } elseif ($openWidth > 9438 && $openWidth <= 9500) {
            $d[3500] = 1;
        }

        // col43 (4000mm)
        if ($openWidth > 3500 && $openWidth <= 4000) {
            $d[4000] = 1;
        } elseif ($openWidth > 4876 && $openWidth <= 5219) {
            $d[4000] = 1;
        } elseif ($openWidth > 6000 && $openWidth <= 6438) {
            $d[4000] = 1;
        } elseif ($openWidth > 7150 && $openWidth <= 7500) {
            $d[4000] = 1;
        } elseif ($openWidth > 7650 && $openWidth <= 8000) {
            $d[4000] = 2;
        } elseif ($openWidth > 8000 && $openWidth <= 8150) {
            $d[4000] = 1;
        } elseif ($openWidth > 8438 && $openWidth <= 8876) {
            $d[4000] = 1;
        } elseif ($openWidth > 9026 && $openWidth <= 9219) {
            $d[4000] = 2;
        } elseif ($openWidth > 9500 && $openWidth <= 10000) {
            $d[4000] = 1;
        } elseif ($openWidth > 10150 && $openWidth <= 10438) {
            $d[4000] = 2;
        } elseif ($openWidth > 10738 && $openWidth <= 11000) {
            $d[4000] = 2;
        }

        // col44 (4150mm)
        if ($openWidth > 4000 && $openWidth <= 4150) {
            $d[4150] = 1;
        } elseif ($openWidth > 5219 && $openWidth <= 5369) {
            $d[4150] = 1;
        } elseif ($openWidth > 6500 && $openWidth <= 6588) {
            $d[4150] = 1;
        } elseif ($openWidth > 7000 && $openWidth <= 7150) {
            $d[4150] = 1;
        } elseif ($openWidth > 7500 && $openWidth <= 7650) {
            $d[4150] = 1;
        } elseif ($openWidth > 8000 && $openWidth <= 8150) {
            $d[4150] = 1;
        } elseif ($openWidth > 8150 && $openWidth <= 8300) {
            $d[4150] = 2;
        } elseif ($openWidth > 9000 && $openWidth <= 9026) {
            $d[4150] = 1;
        } elseif ($openWidth > 10000 && $openWidth <= 10150) {
            $d[4150] = 1;
        } elseif ($openWidth > 10438 && $openWidth <= 10738) {
            $d[4150] = 2;
        }

        return $d;
    }

    // ─────────────────────────────────────────────────
    // smokeBarrier 섹션
    // ─────────────────────────────────────────────────

    /**
     * 연기차단재 섹션 (레거시 Slat_updateCol24~29, Slat_updateCol48 동일)
     *
     * W50 (레일용): col24 = open_height + 250 → 범위별 표준 길이, qty = 2 × 셔터수
     * W80 (케이스용): col38 = open_width + 240, col48 = floor(col38*2/3000 + 1) × 셔터수
     */
    private function buildSmokeBarrier(array $dimGroups): array
    {
        $w50Combined = [];
        $w80Qty = 0;

        foreach ($dimGroups as $group) {
            $height = $group['height'];
            $width = $group['width'];
            $nodeQty = $group['qty'];

            // W50: col24 = open_height + 250 → 범위별 표준 길이
            $col24 = $height + 250;
            $w50Length = null;

            if ($col24 <= 2438) {
                $w50Length = 2438;
            } elseif ($col24 <= 3000) {
                $w50Length = 3000;
            } elseif ($col24 <= 3500) {
                $w50Length = 3500;
            } elseif ($col24 <= 4000) {
                $w50Length = 4000;
            } elseif ($col24 <= 4300) {
                $w50Length = 4300;
            }
            // > 4300: W50 없음

            if ($w50Length !== null) {
                $w50Combined[$w50Length] = ($w50Combined[$w50Length] ?? 0) + 2 * $nodeQty;
            }

            // W80: col38 = open_width + 240, col48 = floor(col38*2/3000 + 1) × 셔터수
            $col38 = $width + 240;
            $w80PerNode = (int) floor(($col38 * 2 / 3000) + 1);
            $w80Qty += $w80PerNode * $nodeQty;
        }

        // W50 결과 조립
        $w50Result = [];
        ksort($w50Combined);
        foreach ($w50Combined as $length => $qty) {
            if ($qty > 0) {
                $w50Result[] = ['length' => $length, 'quantity' => $qty];
            }
        }

        return [
            'w50' => $w50Result,
            'w80Qty' => $w80Qty,
        ];
    }

    // ─────────────────────────────────────────────────
    // 재질 매핑 (프론트 getMaterialMapping 동일 로직)
    // ─────────────────────────────────────────────────

    /**
     * @see react/src/components/production/WorkOrders/documents/bending/utils.ts:77
     */
    private function getMaterialMapping(string $productCode, string $finishMaterial): array
    {
        // Group 1: SUS 전용 (KQTS01, KSS01, KSS02)
        if (in_array($productCode, ['KQTS01', 'KSS01', 'KSS02'])) {
            return [
                'guideRailFinish' => 'SUS 1.2T',
                'bodyMaterial' => 'EGI 1.55T',
                'guideRailExtraFinish' => '',
                'bottomBarFinish' => 'SUS 1.5T',
                'bottomBarExtraFinish' => '없음',
            ];
        }

        // Group 2: KTE01 (마감유형 분기)
        if ($productCode === 'KTE01') {
            $isSUS = $finishMaterial === 'SUS마감';

            return [
                'guideRailFinish' => 'EGI 1.55T',
                'bodyMaterial' => 'EGI 1.55T',
                'guideRailExtraFinish' => $isSUS ? 'SUS 1.2T' : '',
                'bottomBarFinish' => 'EGI 1.55T',
                'bottomBarExtraFinish' => $isSUS ? 'SUS 1.2T' : '없음',
            ];
        }

        // Group 3: 기타 (KSE01, KWE01 등)
        $isSUS = str_contains($finishMaterial, 'SUS');

        return [
            'guideRailFinish' => 'EGI 1.55T',
            'bodyMaterial' => 'EGI 1.55T',
            'guideRailExtraFinish' => $isSUS ? 'SUS 1.2T' : '',
            'bottomBarFinish' => 'EGI 1.55T',
            'bottomBarExtraFinish' => $isSUS ? 'SUS 1.2T' : '없음',
        ];
    }

    // ─────────────────────────────────────────────────
    // 유틸리티 메서드
    // ─────────────────────────────────────────────────

    /**
     * 모터 BOM item_code에서 용량 추출
     * EST-MOTOR-220V-500K → "500K"
     */
    private function extractMotorCapacity(?array $motorBom): ?string
    {
        if (! $motorBom) {
            return null;
        }
        $code = $motorBom['item_code'] ?? '';
        if (preg_match('/(\d+K)$/', $code, $matches)) {
            return $matches[1];
        }

        return null;
    }

    /**
     * 모터용량 → 셔터박스 조립 크기 매핑
     * 레거시 체인: 모터용량(col20) → 브라켓(col21) → 박스크기(col37)
     *
     * @see 5130/estimate/common/common_addrowJS.php Slat_updateCol21(), Slat_updateCol37()
     */
    private function getShutterBoxSize(string $motorCapacity): string
    {
        return match ($motorCapacity) {
            '300K', '400K' => '650*550',
            '500K', '600K' => '700*600',
            '800K', '1000K' => '780*650',
            default => '650*550',
        };
    }

    /**
     * 가이드레일 item_code에서 baseSize 추출
     * BD-가이드레일-KSS01-SUS-120*70 → "120*70"
     */
    private function extractBaseSize(string $itemCode): string
    {
        $parts = explode('-', $itemCode);
        $last = end($parts);

        // 마지막 세그먼트가 "숫자*숫자" 패턴인지 확인
        if (preg_match('/^\d+\*\d+$/', $last)) {
            return $last;
        }

        return '';
    }

    /**
     * item_code 마지막 사이즈 세그먼트 추출
     * BD-L-BAR-KSS01-17*60 → "17*60"
     */
    private function extractLastSize(string $itemCode): string
    {
        return $this->extractBaseSize($itemCode);
    }

    /**
     * item_code에서 마감재 정보 추출 (L-BAR 등)
     */
    private function extractFinishFromCode(string $itemCode, array $materials): string
    {
        if (str_contains($itemCode, '-SUS-')) {
            return 'SUS';
        }
        if (str_contains($itemCode, '-EGI-')) {
            return 'EGI';
        }

        // BOM 코드에 마감재 표시 없으면 재질 매핑에서 추출
        return str_contains($materials['guideRailFinish'], 'SUS') ? 'SUS' : 'EGI';
    }

    /**
     * height 기준 lengthQuantities (common 섹션용)
     */
    private function heightLengthQuantities(array $dimGroups): array
    {
        $result = [];
        $grouped = [];

        foreach ($dimGroups as $group) {
            $h = $group['height'];
            $grouped[$h] = ($grouped[$h] ?? 0) + $group['qty'];
        }

        foreach ($grouped as $length => $qty) {
            $result[] = ['length' => $length, 'quantity' => $qty];
        }

        return $result;
    }

    /**
     * height 기준 lengthData (guideRail, smokeBarrier 섹션용)
     * 오픈높이를 표준 원자재 길이로 버킷팅
     */
    private function heightLengthData(array $dimGroups): array
    {
        return $this->bucketedLengthData($dimGroups, 'height', self::GUIDE_RAIL_LENGTHS);
    }

    /**
     * width 기준 lengthData (shutterBox 섹션용)
     * 오픈폭을 표준 원자재 길이로 버킷팅
     */
    private function widthLengthData(array $dimGroups): array
    {
        return $this->bucketedLengthData($dimGroups, 'width', self::SHUTTER_BOX_LENGTHS);
    }

    /**
     * 치수를 표준 원자재 길이로 버킷팅하여 그룹핑
     */
    private function bucketedLengthData(array $dimGroups, string $dimKey, array $buckets): array
    {
        $result = [];
        $grouped = [];

        foreach ($dimGroups as $group) {
            $raw = (int) $group[$dimKey];
            $bucketed = $this->bucketToStandardLength($raw, $buckets);
            $grouped[$bucketed] = ($grouped[$bucketed] ?? 0) + $group['qty'];
        }

        foreach ($grouped as $length => $qty) {
            $result[] = ['length' => $length, 'quantity' => $qty];
        }

        return $result;
    }

    /**
     * 오픈폭에 따른 하단마감재(하장바) 3000/4000 수량 배분
     * 레거시 5130/estimate/common/common_addrowJS.php Slat_updateCol49to51() 동일
     *
     * @return array{0: int, 1: int} [3000mm수량, 4000mm수량]
     */
    private function bottomBarDistribution(int $openWidth): array
    {
        // [3000mm 기본수, 4000mm 기본수]
        if ($openWidth <= 3000) {
            return [1, 0];
        }
        if ($openWidth <= 4000) {
            return [0, 1];
        }
        if ($openWidth <= 6000) {
            return [2, 0];
        }
        if ($openWidth <= 7000) {
            return [1, 1];
        }
        if ($openWidth <= 8000) {
            return [0, 2];
        }
        if ($openWidth <= 9000) {
            return [3, 0];
        }
        if ($openWidth <= 10000) {
            return [2, 1];
        }
        if ($openWidth <= 11000) {
            return [1, 2];
        }
        if ($openWidth <= 12000) {
            return [0, 3];
        }

        // 12000 초과: 4000mm 기준 올림
        return [0, (int) ceil($openWidth / 4000)];
    }

    /**
     * 제품코드별 실제 하부BASE 물리 치수 반환
     * 대형 프로파일 (KQTS01, KTE01): 벽면 BASE = 135*130
     * 소형 프로파일 (KSS01, KSS02 등): 벽면 BASE = 135*80
     * 측면형 BASE는 항상 135*130
     */
    private function getBaseDimensions(string $productCode): array
    {
        // 레거시: 벽면형 BASE = 135*80 (line 519), 측면형 BASE = 135*130 (line 626)
        // 제품코드와 무관하게 고정
        return [
            'wall' => '135*80',
            'side' => '135*130',
        ];
    }

    /**
     * 치수를 표준 원자재 길이로 변환 (올림 버킷팅)
     * dimension 이상인 최소 표준 길이 반환, 초과 시 원본 반환
     */
    private function bucketToStandardLength(int $dimension, array $buckets): int
    {
        foreach ($buckets as $bucket) {
            if ($bucket >= $dimension) {
                return $bucket;
            }
        }

        return $dimension;
    }
}
