<?php

namespace App\Services;

use App\Constants\SystemFields;
use App\Exceptions\DuplicateCodeException;
use App\Helpers\ItemTypeHelper;
use App\Models\Commons\File;
use App\Models\ItemMaster\ItemField;
use App\Models\Materials\Material;
use App\Models\Products\Product;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class ItemsService extends Service
{
    /**
     * 고정 필드 목록 조회 (SystemFields + ItemField 기반)
     */
    private function getKnownFields(string $sourceTable): array
    {
        $tenantId = $this->tenantId();

        // 1. SystemFields에서 테이블 고정 컬럼
        $systemFields = SystemFields::getReservedKeys($sourceTable);

        // 2. ItemField에서 storage_type='column'인 필드의 field_key 조회
        $columnFields = ItemField::where('tenant_id', $tenantId)
            ->where('source_table', $sourceTable)
            ->where('storage_type', 'column')
            ->whereNotNull('field_key')
            ->pluck('field_key')
            ->toArray();

        // 3. 추가적인 API 전용 필드
        $apiFields = ['item_type', 'type_code', 'bom', 'product_type'];

        return array_unique(array_merge($systemFields, $columnFields, $apiFields));
    }

    /**
     * 동적 필드 추출
     */
    private function extractDynamicOptions(array $params, string $sourceTable): array
    {
        $knownFields = $this->getKnownFields($sourceTable);

        $dynamicOptions = [];
        foreach ($params as $key => $value) {
            if (! in_array($key, $knownFields) && $value !== null && $value !== '') {
                $dynamicOptions[$key] = $value;
            }
        }

        return $dynamicOptions;
    }

    /**
     * 기존 options와 동적 필드 병합
     */
    private function mergeOptionsWithDynamic($existingOptions, array $dynamicOptions): array
    {
        if (! is_array($existingOptions) || empty($existingOptions)) {
            return $dynamicOptions;
        }

        $isAssoc = array_keys($existingOptions) !== range(0, count($existingOptions) - 1);

        if ($isAssoc) {
            return array_merge($existingOptions, $dynamicOptions);
        }

        foreach ($dynamicOptions as $key => $value) {
            $existingOptions[] = ['label' => $key, 'value' => $value];
        }

        return $existingOptions;
    }

    /**
     * options 정규화 [{label, value, unit}]
     */
    private function normalizeOptions(?array $in): ?array
    {
        if (! $in) {
            return null;
        }

        $isAssoc = array_keys($in) !== range(0, count($in) - 1);

        if ($isAssoc) {
            $out = [];
            foreach ($in as $k => $v) {
                $label = trim((string) $k);
                $value = is_scalar($v) ? trim((string) $v) : json_encode($v, JSON_UNESCAPED_UNICODE);
                if ($label !== '' || $value !== '') {
                    $out[] = ['label' => $label, 'value' => $value, 'unit' => ''];
                }
            }

            return $out ?: null;
        }

        $out = [];
        foreach ($in as $a) {
            if (! is_array($a)) {
                continue;
            }
            $label = trim((string) ($a['label'] ?? ''));
            $value = trim((string) ($a['value'] ?? ''));
            $unit = trim((string) ($a['unit'] ?? ''));

            if ($label === '' && $value === '') {
                continue;
            }

            $out[] = ['label' => $label, 'value' => $value, 'unit' => $unit];
        }

        return $out ?: null;
    }

    /**
     * 동적 필드를 options에 병합하고 정규화
     */
    private function processDynamicOptions(array &$data, string $sourceTable): void
    {
        $dynamicOptions = $this->extractDynamicOptions($data, $sourceTable);
        if (! empty($dynamicOptions)) {
            $existingOptions = $data['options'] ?? [];
            $data['options'] = $this->mergeOptionsWithDynamic($existingOptions, $dynamicOptions);
        }

        if (isset($data['options'])) {
            $data['options'] = $this->normalizeOptions($data['options']);
        }
    }

    /**
     * BOM 데이터에서 child_item_id, child_item_type, quantity만 추출
     *
     * @param  array|null  $bomData  BOM 데이터 배열
     * @return array|null [{child_item_id, child_item_type, quantity}, ...]
     */
    private function extractBomData(?array $bomData): ?array
    {
        if (empty($bomData)) {
            return null;
        }

        $extracted = [];
        foreach ($bomData as $item) {
            if (! is_array($item)) {
                continue;
            }

            $childItemId = $item['child_item_id'] ?? null;
            $childItemType = $item['child_item_type'] ?? $item['ref_type'] ?? 'PRODUCT';
            $quantity = $item['quantity'] ?? null;

            if ($childItemId === null) {
                continue;
            }

            // child_item_type 정규화 (PRODUCT/MATERIAL)
            $childItemType = strtoupper($childItemType);
            if (! in_array($childItemType, ['PRODUCT', 'MATERIAL'])) {
                $childItemType = 'PRODUCT';
            }

            $extracted[] = [
                'child_item_id' => (int) $childItemId,
                'child_item_type' => $childItemType,
                'quantity' => $quantity !== null ? (float) $quantity : 1,
            ];
        }

        return empty($extracted) ? null : $extracted;
    }

    /**
     * BOM 데이터 확장 (child_item 상세 정보 포함)
     *
     * DB에 저장된 [{child_item_id, child_item_type, quantity}] 형태를
     * [{child_item_id, child_item_type, child_item_code, child_item_name, quantity, unit, specification?}]
     * 형태로 확장하여 반환
     *
     * @param  array  $bomData  BOM 데이터 배열 [{child_item_id, child_item_type, quantity}, ...]
     * @param  int  $tenantId  테넌트 ID
     * @return array 확장된 BOM 데이터
     */
    private function expandBomData(array $bomData, int $tenantId): array
    {
        if (empty($bomData)) {
            return [];
        }

        // child_item_type별로 ID 분리
        $productIds = [];
        $materialIds = [];

        foreach ($bomData as $item) {
            $childId = $item['child_item_id'] ?? null;
            $childType = strtoupper($item['child_item_type'] ?? 'PRODUCT');

            if ($childId === null) {
                continue;
            }

            if ($childType === 'MATERIAL') {
                $materialIds[] = $childId;
            } else {
                $productIds[] = $childId;
            }
        }

        // Products에서 조회 (FG, PT)
        $products = collect([]);
        if (! empty($productIds)) {
            $products = Product::query()
                ->where('tenant_id', $tenantId)
                ->whereIn('id', $productIds)
                ->get(['id', 'code', 'name', 'unit'])
                ->keyBy('id');
        }

        // Materials에서 조회 (SM, RM, CS)
        $materials = collect([]);
        if (! empty($materialIds)) {
            $materials = Material::query()
                ->where('tenant_id', $tenantId)
                ->whereIn('id', $materialIds)
                ->get(['id', 'material_code', 'name', 'unit', 'specification'])
                ->keyBy('id');
        }

        // BOM 데이터 확장
        $expanded = [];
        foreach ($bomData as $item) {
            $childId = $item['child_item_id'] ?? null;
            $childType = strtoupper($item['child_item_type'] ?? 'PRODUCT');

            if ($childId === null) {
                continue;
            }

            $result = [
                'child_item_id' => (int) $childId,
                'child_item_type' => $childType,
                'quantity' => $item['quantity'] ?? 1,
            ];

            // child_item_type에 따라 조회
            if ($childType === 'MATERIAL' && isset($materials[$childId])) {
                $material = $materials[$childId];
                $result['child_item_code'] = $material->material_code;
                $result['child_item_name'] = $material->name;
                $result['unit'] = $material->unit;
                if ($material->specification) {
                    $result['specification'] = $material->specification;
                }
            } elseif ($childType === 'PRODUCT' && isset($products[$childId])) {
                $product = $products[$childId];
                $result['child_item_code'] = $product->code;
                $result['child_item_name'] = $product->name;
                $result['unit'] = $product->unit;
            } else {
                // 해당하는 품목이 없으면 null 처리
                $result['child_item_code'] = null;
                $result['child_item_name'] = null;
                $result['unit'] = null;
            }

            $expanded[] = $result;
        }

        return $expanded;
    }

    /**
     * 통합 품목 조회 (materials + products UNION)
     *
     * @param  array  $filters  필터 조건 (type, search, category_id, page, size)
     * @param  int  $perPage  페이지당 항목 수
     * @param  bool  $includeDeleted  soft delete 포함 여부
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function getItems(array $filters = [], int $perPage = 20, bool $includeDeleted = false)
    {
        $tenantId = $this->tenantId();

        // 필터 파라미터 추출
        $types = $filters['type'] ?? ['FG', 'PT', 'SM', 'RM', 'CS'];
        $search = trim($filters['search'] ?? $filters['q'] ?? '');
        $categoryId = $filters['category_id'] ?? null;
        $isActive = $filters['is_active'] ?? null; // null: 전체, true/1: 활성만, false/0: 비활성만

        // 타입을 배열로 변환 (문자열인 경우 쉼표로 분리)
        if (is_string($types)) {
            $types = array_map('trim', explode(',', $types));
        }

        // Product 타입 (FG, PT)
        $productTypes = array_intersect(['FG', 'PT'], $types);
        // Material 타입 (SM, RM, CS)
        $materialTypes = array_intersect(['SM', 'RM', 'CS'], $types);

        // Products 쿼리 (FG, PT 타입만)
        $productsQuery = null;
        if (! empty($productTypes)) {
            $productsQuery = Product::query()
                ->where('tenant_id', $tenantId)
                ->whereIn('product_type', $productTypes);

            // is_active 필터 적용 (null이면 전체 조회)
            if ($isActive !== null) {
                $productsQuery->where('is_active', filter_var($isActive, FILTER_VALIDATE_BOOLEAN) ? 1 : 0);
            }

            $productsQuery->select([
                'id',
                'product_type as item_type',
                'code',
                'name',
                DB::raw('NULL as specification'),
                'unit',
                'category_id',
                'product_type as type_code',
                'attributes',
                'is_active',
                'created_at',
                'deleted_at',
            ]);

            // soft delete 포함
            if ($includeDeleted) {
                $productsQuery->withTrashed();
            }

            // 검색 조건
            if ($search !== '') {
                $productsQuery->where(function ($q) use ($search) {
                    $q->where('name', 'like', "%{$search}%")
                        ->orWhere('code', 'like', "%{$search}%");
                });
            }

            // 카테고리 필터
            if ($categoryId) {
                $productsQuery->where('category_id', (int) $categoryId);
            }
        }

        // Materials 쿼리 (SM, RM, CS 타입)
        $materialsQuery = null;
        if (! empty($materialTypes)) {
            $materialsQuery = Material::query()
                ->where('tenant_id', $tenantId)
                ->whereIn('material_type', $materialTypes);

            // is_active 필터 적용 (null이면 전체 조회)
            if ($isActive !== null) {
                $materialsQuery->where('is_active', filter_var($isActive, FILTER_VALIDATE_BOOLEAN) ? 1 : 0);
            }

            $materialsQuery->select([
                'id',
                'material_type as item_type',
                'material_code as code',
                'name',
                'specification',
                'unit',
                'category_id',
                'material_type as type_code',
                'attributes',
                'is_active',
                'created_at',
                'deleted_at',
            ]);

            // soft delete 포함
            if ($includeDeleted) {
                $materialsQuery->withTrashed();
            }

            // 검색 조건
            if ($search !== '') {
                $materialsQuery->where(function ($q) use ($search) {
                    $q->where('name', 'like', "%{$search}%")
                        ->orWhere('item_name', 'like', "%{$search}%")
                        ->orWhere('material_code', 'like', "%{$search}%")
                        ->orWhere('search_tag', 'like', "%{$search}%");
                });
            }

            // 카테고리 필터
            if ($categoryId) {
                $materialsQuery->where('category_id', (int) $categoryId);
            }
        }

        // 각 쿼리 실행 후 merge (UNION 바인딩 문제 방지)
        $items = collect([]);

        if ($productsQuery) {
            $products = $productsQuery->get();
            $items = $items->merge($products);
        }

        if ($materialsQuery) {
            $materials = $materialsQuery->get();
            $items = $items->merge($materials);
        }

        // 정렬 (created_at 기준 내림차순)
        $items = $items->sortByDesc('created_at')->values();

        // attributes 플랫 전개
        $items = $items->map(function ($item) {
            $data = $item->toArray();
            $attributes = $data['attributes'] ?? [];
            if (is_string($attributes)) {
                $attributes = json_decode($attributes, true) ?? [];
            }
            unset($data['attributes']);

            return array_merge($data, $attributes);
        });

        // 페이지네이션 처리
        $page = request()->input('page', 1);
        $offset = ($page - 1) * $perPage;
        $total = $items->count();

        $paginatedItems = $items->slice($offset, $perPage)->values();

        return new \Illuminate\Pagination\LengthAwarePaginator(
            $paginatedItems,
            $total,
            $perPage,
            $page,
            ['path' => request()->url(), 'query' => request()->query()]
        );
    }

    /**
     * 단일 품목 조회
     *
     * @param  int  $id  품목 ID
     * @param  string  $itemType  품목 유형 코드 (FG/PT/SM/RM/CS)
     * @param  bool  $includePrice  가격 정보 포함 여부
     * @param  int|null  $clientId  고객 ID (가격 조회 시)
     * @param  string|null  $priceDate  기준일 (가격 조회 시)
     * @return array 품목 데이터 (item_type, prices 포함)
     */
    public function getItem(
        int $id,
        string $itemType = 'FG',
        bool $includePrice = false,
        ?int $clientId = null,
        ?string $priceDate = null
    ): array {
        $tenantId = $this->tenantId();
        $itemType = strtoupper($itemType);

        // item_type으로 source_table 결정
        if (ItemTypeHelper::isMaterial($itemType, $tenantId)) {
            $material = Material::query()
                ->with('category:id,name')
                ->where('tenant_id', $tenantId)
                ->find($id);

            if (! $material) {
                throw new NotFoundHttpException(__('error.not_found'));
            }

            $data = $this->flattenAttributes($material->toArray());
            $data['item_type'] = $itemType;
            $data['code'] = $material->material_code;
            $data['type_code'] = $material->material_type;

            // 가격 정보 추가
            if ($includePrice) {
                $data['prices'] = $this->fetchPrices('MATERIAL', $id, $clientId, $priceDate);
            }

            // 파일 정보 추가
            $data['files'] = $this->getItemFiles($id, $tenantId);

            return $data;
        }

        // Product (FG, PT)
        $product = Product::query()
            ->with('category:id,name')
            ->where('tenant_id', $tenantId)
            ->find($id);

        if (! $product) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        $data = $this->flattenAttributes($product->toArray());
        $data['item_type'] = $itemType;
        $data['type_code'] = $product->product_type;

        // BOM 데이터 확장 (child_item 상세 정보 포함)
        if (! empty($data['bom'])) {
            $data['bom'] = $this->expandBomData($data['bom'], $tenantId);
        }

        // 가격 정보 추가
        if ($includePrice) {
            $data['prices'] = $this->fetchPrices('PRODUCT', $id, $clientId, $priceDate);
        }

        // 파일 정보 추가
        $data['files'] = $this->getItemFiles($id, $tenantId);

        return $data;
    }

    /**
     * attributes/options JSON 필드를 최상위로 플랫 전개
     */
    private function flattenAttributes(array $data): array
    {
        // attributes 플랫 전개
        $attributes = $data['attributes'] ?? [];
        if (is_string($attributes)) {
            $attributes = json_decode($attributes, true) ?? [];
        }
        unset($data['attributes']);

        // options 플랫 전개 ([{label, value, unit}] 형태 → {label: value} 형태로 변환)
        $options = $data['options'] ?? [];
        if (is_string($options)) {
            $options = json_decode($options, true) ?? [];
        }
        $flatOptions = [];
        if (is_array($options)) {
            foreach ($options as $opt) {
                if (is_array($opt) && isset($opt['label'])) {
                    $flatOptions[$opt['label']] = $opt['value'] ?? '';
                }
            }
        }
        // options는 원본 유지 (프론트에서 필요할 수 있음)

        return array_merge($data, $attributes, $flatOptions);
    }

    /**
     * 품목의 판매가/매입가 조회
     *
     * @param  string  $itemType  'PRODUCT' | 'MATERIAL'
     * @param  int  $itemId  품목 ID
     * @param  int|null  $clientId  고객 ID
     * @param  string|null  $priceDate  기준일
     * @return array ['sale' => array, 'purchase' => array]
     */
    private function fetchPrices(string $itemType, int $itemId, ?int $clientId, ?string $priceDate): array
    {
        // PricingService DI가 없으므로 직접 생성
        $pricingService = app(\App\Services\Pricing\PricingService::class);

        $salePrice = $pricingService->getPriceByType($itemType, $itemId, 'SALE', $clientId, $priceDate);
        $purchasePrice = $pricingService->getPriceByType($itemType, $itemId, 'PURCHASE', $clientId, $priceDate);

        return [
            'sale' => $salePrice,
            'purchase' => $purchasePrice,
        ];
    }

    /**
     * 품목 생성 (타입에 따라 Products 또는 Materials 테이블에 저장)
     *
     * - FG, PT → Products 테이블
     * - SM, RM, CS → Materials 테이블
     *
     * @param  array  $data  검증된 데이터
     */
    public function createItem(array $data): Product|Material
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();
        $itemType = strtoupper($data['product_type'] ?? 'FG');

        // Materials 타입 (SM, RM, CS)
        if (in_array($itemType, ['SM', 'RM', 'CS'])) {
            return $this->createMaterial($data, $tenantId, $userId, $itemType);
        }

        // Products 타입 (FG, PT) - 기본값
        return $this->createProduct($data, $tenantId, $userId);
    }

    /**
     * Product 생성 (FG, PT)
     */
    private function createProduct(array $data, int $tenantId, int $userId): Product
    {
        // 품목 코드 중복 체크
        $code = $data['code'] ?? '';
        $existingProduct = Product::withTrashed()
            ->where('tenant_id', $tenantId)
            ->where('code', $code)
            ->first();

        if ($existingProduct) {
            throw new DuplicateCodeException($code, $existingProduct->id);
        }

        // 동적 필드를 options에 병합
        $this->processDynamicOptions($data, 'products');

        // BOM 데이터 처리 (child_item_id, quantity만 추출)
        $bomData = $this->extractBomData($data['bom'] ?? null);

        $payload = $data;
        $payload['tenant_id'] = $tenantId;
        $payload['created_by'] = $userId;
        $payload['is_active'] = $payload['is_active'] ?? true;
        $payload['is_sellable'] = $payload['is_sellable'] ?? true;
        $payload['is_purchasable'] = $payload['is_purchasable'] ?? false;
        $payload['is_producible'] = $payload['is_producible'] ?? false;
        $payload['bom'] = $bomData;

        return Product::create($payload);
    }

    /**
     * Material 생성 (SM, RM, CS)
     */
    private function createMaterial(array $data, int $tenantId, int $userId, string $materialType): Material
    {
        // 품목 코드 중복 체크
        $code = $data['code'] ?? $data['material_code'] ?? '';
        $existingMaterial = Material::withTrashed()
            ->where('tenant_id', $tenantId)
            ->where('material_code', $code)
            ->first();

        if ($existingMaterial) {
            throw new DuplicateCodeException($code, $existingMaterial->id);
        }

        // 동적 필드를 options에 병합
        $this->processDynamicOptions($data, 'materials');

        $payload = [
            'tenant_id' => $tenantId,
            'created_by' => $userId,
            'material_type' => $materialType,
            'material_code' => $code,
            'name' => $data['name'],
            'unit' => $data['unit'],
            'category_id' => $data['category_id'] ?? null,
            'item_name' => $data['item_name'] ?? $data['name'],
            'specification' => $data['specification'] ?? $data['description'] ?? null,
            'is_inspection' => $data['is_inspection'] ?? 'N',
            'search_tag' => $data['search_tag'] ?? null,
            'remarks' => $data['remarks'] ?? null,
            'attributes' => $data['attributes'] ?? null,
            'options' => $data['options'] ?? null,
            'is_active' => $data['is_active'] ?? true,
        ];

        return Material::create($payload);
    }

    /**
     * 품목 수정 (Product/Material 통합)
     *
     * @param  int  $id  품목 ID
     * @param  array  $data  검증된 데이터 (item_type 필수)
     */
    public function updateItem(int $id, array $data): Product|Material
    {
        $tenantId = $this->tenantId();
        $itemType = strtoupper($data['item_type'] ?? 'FG');

        if (ItemTypeHelper::isMaterial($itemType, $tenantId)) {
            return $this->updateMaterial($id, $data);
        }

        return $this->updateProduct($id, $data);
    }

    /**
     * Product 수정 (FG, PT)
     */
    private function updateProduct(int $id, array $data): Product
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        $product = Product::query()
            ->where('tenant_id', $tenantId)
            ->find($id);

        if (! $product) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        // 코드 변경 시 중복 체크
        if (isset($data['code']) && $data['code'] !== $product->code) {
            $existingProduct = Product::query()
                ->where('tenant_id', $tenantId)
                ->where('code', $data['code'])
                ->where('id', '!=', $product->id)
                ->first();

            if ($existingProduct) {
                throw new DuplicateCodeException($data['code'], $existingProduct->id);
            }
        }

        // 동적 필드를 options에 병합
        $this->processDynamicOptions($data, 'products');

        // BOM 데이터 처리 (bom 키가 있을 때만)
        if (array_key_exists('bom', $data)) {
            $data['bom'] = $this->extractBomData($data['bom']);
        }

        // item_type은 DB 필드가 아니므로 제거
        unset($data['item_type']);
        $data['updated_by'] = $userId;
        $product->update($data);

        return $product->refresh();
    }

    /**
     * Material 수정 (SM, RM, CS)
     */
    private function updateMaterial(int $id, array $data): Material
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        $material = Material::query()
            ->where('tenant_id', $tenantId)
            ->find($id);

        if (! $material) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        // 코드 변경 시 중복 체크
        $newCode = $data['material_code'] ?? $data['code'] ?? null;
        if ($newCode && $newCode !== $material->material_code) {
            $existingMaterial = Material::query()
                ->where('tenant_id', $tenantId)
                ->where('material_code', $newCode)
                ->where('id', '!=', $material->id)
                ->first();

            if ($existingMaterial) {
                throw new DuplicateCodeException($newCode, $existingMaterial->id);
            }
            $data['material_code'] = $newCode;
        }

        // 동적 필드를 options에 병합
        $this->processDynamicOptions($data, 'materials');

        // item_type, code는 DB 필드가 아니므로 제거
        unset($data['item_type'], $data['code']);
        $data['updated_by'] = $userId;
        $material->update($data);

        return $material->refresh();
    }

    /**
     * 품목 삭제 (Product/Material 통합, Force Delete)
     *
     * - 이미 삭제된 품목은 404 반환
     * - 다른 테이블에서 사용 중이면 삭제 불가
     * - 사용 안함 → Force Delete (영구 삭제)
     *
     * @param  int  $id  품목 ID
     * @param  string  $itemType  품목 유형 코드 (FG/PT/SM/RM/CS)
     */
    public function deleteItem(int $id, string $itemType = 'FG'): void
    {
        $tenantId = $this->tenantId();
        $itemType = strtoupper($itemType);

        if (ItemTypeHelper::isMaterial($itemType, $tenantId)) {
            $this->deleteMaterial($id);

            return;
        }

        $this->deleteProduct($id);
    }

    /**
     * Product 삭제 (FG, PT) - Force Delete
     *
     * - 사용 중이면 삭제 불가 (에러)
     * - 사용 안함 → 영구 삭제
     */
    private function deleteProduct(int $id): void
    {
        $tenantId = $this->tenantId();

        $product = Product::query()
            ->where('tenant_id', $tenantId)
            ->find($id);

        if (! $product) {
            throw new NotFoundHttpException(__('error.item.not_found'));
        }

        // 사용 여부 종합 체크
        $usageResult = $this->checkProductUsage($tenantId, $id);
        if ($usageResult['is_used']) {
            $usageMessage = $this->formatUsageMessage($usageResult['usage']);
            throw new BadRequestHttpException(__('error.item.in_use', ['usage' => $usageMessage]));
        }

        // 사용 안함 → Force Delete
        $product->forceDelete();
    }

    /**
     * Material 삭제 (SM, RM, CS) - Force Delete
     *
     * - 사용 중이면 삭제 불가 (에러)
     * - 사용 안함 → 영구 삭제
     */
    private function deleteMaterial(int $id): void
    {
        $tenantId = $this->tenantId();

        $material = Material::query()
            ->where('tenant_id', $tenantId)
            ->find($id);

        if (! $material) {
            throw new NotFoundHttpException(__('error.item.not_found'));
        }

        // 사용 여부 종합 체크
        $usageResult = $this->checkMaterialUsage($tenantId, $id);
        if ($usageResult['is_used']) {
            $usageMessage = $this->formatUsageMessage($usageResult['usage']);
            throw new BadRequestHttpException(__('error.item.in_use', ['usage' => $usageMessage]));
        }

        // 사용 안함 → Force Delete
        $material->forceDelete();
    }

    /**
     * Product 사용 여부 종합 체크 (모든 참조 테이블)
     *
     * @param  int  $tenantId  테넌트 ID
     * @param  int  $productId  제품 ID
     * @return array ['is_used' => bool, 'usage' => ['table' => count, ...]]
     */
    private function checkProductUsage(int $tenantId, int $productId): array
    {
        $usage = [];

        // 1. BOM 구성품으로 사용 (product_components.ref_type=PRODUCT)
        $bomComponentCount = \App\Models\Products\ProductComponent::query()
            ->where('tenant_id', $tenantId)
            ->where('ref_type', 'PRODUCT')
            ->where('ref_id', $productId)
            ->count();
        if ($bomComponentCount > 0) {
            $usage['bom_components'] = $bomComponentCount;
        }

        // 2. BOM 상위품목으로 사용 (product_components.parent_product_id)
        $bomParentCount = \App\Models\Products\ProductComponent::query()
            ->where('tenant_id', $tenantId)
            ->where('parent_product_id', $productId)
            ->count();
        if ($bomParentCount > 0) {
            $usage['bom_parent'] = $bomParentCount;
        }

        // 3. BOM 템플릿 항목 (bom_template_items.ref_type=PRODUCT)
        $bomTemplateCount = \App\Models\Design\BomTemplateItem::query()
            ->where('tenant_id', $tenantId)
            ->where('ref_type', 'PRODUCT')
            ->where('ref_id', $productId)
            ->count();
        if ($bomTemplateCount > 0) {
            $usage['bom_templates'] = $bomTemplateCount;
        }

        // 4. 주문 (orders.product_id)
        $orderCount = \App\Models\Orders\Order::query()
            ->where('tenant_id', $tenantId)
            ->where('product_id', $productId)
            ->count();
        if ($orderCount > 0) {
            $usage['orders'] = $orderCount;
        }

        // 5. 주문 항목 (order_items.product_id)
        $orderItemCount = \App\Models\Orders\OrderItem::query()
            ->where('tenant_id', $tenantId)
            ->where('product_id', $productId)
            ->count();
        if ($orderItemCount > 0) {
            $usage['order_items'] = $orderItemCount;
        }

        // 6. 견적 (quotes.product_id)
        $quoteCount = \App\Models\Quote\Quote::query()
            ->where('tenant_id', $tenantId)
            ->where('product_id', $productId)
            ->count();
        if ($quoteCount > 0) {
            $usage['quotes'] = $quoteCount;
        }

        return [
            'is_used' => ! empty($usage),
            'usage' => $usage,
        ];
    }

    /**
     * Material 사용 여부 종합 체크 (모든 참조 테이블)
     *
     * @param  int  $tenantId  테넌트 ID
     * @param  int  $materialId  자재 ID
     * @return array ['is_used' => bool, 'usage' => ['table' => count, ...]]
     */
    private function checkMaterialUsage(int $tenantId, int $materialId): array
    {
        $usage = [];

        // 1. BOM 구성품으로 사용 (product_components.ref_type=MATERIAL)
        $bomComponentCount = \App\Models\Products\ProductComponent::query()
            ->where('tenant_id', $tenantId)
            ->where('ref_type', 'MATERIAL')
            ->where('ref_id', $materialId)
            ->count();
        if ($bomComponentCount > 0) {
            $usage['bom_components'] = $bomComponentCount;
        }

        // 2. BOM 템플릿 항목 (bom_template_items.ref_type=MATERIAL)
        $bomTemplateCount = \App\Models\Design\BomTemplateItem::query()
            ->where('tenant_id', $tenantId)
            ->where('ref_type', 'MATERIAL')
            ->where('ref_id', $materialId)
            ->count();
        if ($bomTemplateCount > 0) {
            $usage['bom_templates'] = $bomTemplateCount;
        }

        // 3. 자재 입고 (material_receipts.material_id)
        $receiptCount = \App\Models\Materials\MaterialReceipt::query()
            ->where('tenant_id', $tenantId)
            ->where('material_id', $materialId)
            ->count();
        if ($receiptCount > 0) {
            $usage['receipts'] = $receiptCount;
        }

        // 4. LOT (lots.material_id)
        $lotCount = \App\Models\Qualitys\Lot::query()
            ->where('tenant_id', $tenantId)
            ->where('material_id', $materialId)
            ->count();
        if ($lotCount > 0) {
            $usage['lots'] = $lotCount;
        }

        return [
            'is_used' => ! empty($usage),
            'usage' => $usage,
        ];
    }

    /**
     * 사용처 정보를 한글 메시지로 변환
     */
    private function formatUsageMessage(array $usage): string
    {
        $labels = [
            'bom_components' => 'BOM 구성품',
            'bom_parent' => 'BOM 상위품목',
            'bom_templates' => 'BOM 템플릿',
            'orders' => '주문',
            'order_items' => '주문 항목',
            'quotes' => '견적',
            'receipts' => '입고',
            'lots' => 'LOT',
        ];

        $parts = [];
        foreach ($usage as $key => $count) {
            $label = $labels[$key] ?? $key;
            $parts[] = "{$label} {$count}건";
        }

        return implode(', ', $parts);
    }

    /**
     * 품목 일괄 삭제 (Product/Material 통합, Force Delete)
     *
     * - 다른 테이블에서 사용 중인 품목은 삭제 불가
     * - 사용 안함 → Force Delete (영구 삭제)
     *
     * @param  array  $ids  품목 ID 배열
     * @param  string  $itemType  품목 유형 코드 (FG/PT/SM/RM/CS)
     */
    public function batchDeleteItems(array $ids, string $itemType = 'FG'): void
    {
        $tenantId = $this->tenantId();
        $itemType = strtoupper($itemType);

        if (ItemTypeHelper::isMaterial($itemType, $tenantId)) {
            $this->batchDeleteMaterials($ids);

            return;
        }

        $this->batchDeleteProducts($ids);
    }

    /**
     * Product 일괄 삭제 (FG, PT) - Force Delete
     *
     * - 사용 중인 품목이 있으면 전체 삭제 실패
     * - 모두 사용 안함 → 영구 삭제
     */
    private function batchDeleteProducts(array $ids): void
    {
        $tenantId = $this->tenantId();

        $products = Product::query()
            ->where('tenant_id', $tenantId)
            ->whereIn('id', $ids)
            ->get();

        if ($products->isEmpty()) {
            throw new NotFoundHttpException(__('error.item.not_found'));
        }

        // 사용 중인 품목 체크 (하나라도 사용 중이면 전체 실패)
        $inUseItems = [];
        foreach ($products as $product) {
            $usageResult = $this->checkProductUsage($tenantId, $product->id);
            if ($usageResult['is_used']) {
                $inUseItems[] = [
                    'id' => $product->id,
                    'code' => $product->code,
                    'usage' => $usageResult['usage'],
                ];
            }
        }

        if (! empty($inUseItems)) {
            $codes = array_column($inUseItems, 'code');
            throw new BadRequestHttpException(__('error.item.batch_in_use', [
                'codes' => implode(', ', $codes),
                'count' => count($inUseItems),
            ]));
        }

        // 모두 사용 안함 → Force Delete
        foreach ($products as $product) {
            $product->forceDelete();
        }
    }

    /**
     * Material 일괄 삭제 (SM, RM, CS) - Force Delete
     *
     * - 사용 중인 자재가 있으면 전체 삭제 실패
     * - 모두 사용 안함 → 영구 삭제
     */
    private function batchDeleteMaterials(array $ids): void
    {
        $tenantId = $this->tenantId();

        $materials = Material::query()
            ->where('tenant_id', $tenantId)
            ->whereIn('id', $ids)
            ->get();

        if ($materials->isEmpty()) {
            throw new NotFoundHttpException(__('error.item.not_found'));
        }

        // 사용 중인 자재 체크 (하나라도 사용 중이면 전체 실패)
        $inUseItems = [];
        foreach ($materials as $material) {
            $usageResult = $this->checkMaterialUsage($tenantId, $material->id);
            if ($usageResult['is_used']) {
                $inUseItems[] = [
                    'id' => $material->id,
                    'code' => $material->material_code,
                    'usage' => $usageResult['usage'],
                ];
            }
        }

        if (! empty($inUseItems)) {
            $codes = array_column($inUseItems, 'code');
            throw new BadRequestHttpException(__('error.item.batch_in_use', [
                'codes' => implode(', ', $codes),
                'count' => count($inUseItems),
            ]));
        }

        // 모두 사용 안함 → Force Delete
        foreach ($materials as $material) {
            $material->forceDelete();
        }
    }

    /**
     * 품목의 파일 목록 조회 (field_key별 그룹핑)
     *
     * @param  int  $itemId  품목 ID
     * @param  int  $tenantId  테넌트 ID
     * @return array field_key별로 그룹핑된 파일 목록
     */
    private function getItemFiles(int $itemId, int $tenantId): array
    {
        $files = File::query()
            ->where('tenant_id', $tenantId)
            ->where('document_type', '1')  // ITEM_GROUP_ID
            ->where('document_id', $itemId)
            ->whereNull('deleted_at')
            ->orderBy('created_at', 'desc')
            ->get();

        if ($files->isEmpty()) {
            return [];
        }

        return $files->groupBy('field_key')->map(function ($group) {
            return $group->map(fn ($file) => [
                'id' => $file->id,
                'file_name' => $file->display_name ?? $file->file_name,
                'file_path' => $file->file_path,
            ])->values()->toArray();
        })->toArray();
    }

    /**
     * 품목 상세 조회 (code 기반, BOM 포함 옵션)
     *
     * Product 먼저 조회 → 없으면 Material 조회
     *
     * @param  string  $code  품목 코드
     * @param  bool  $includeBom  BOM 포함 여부
     * @return array 품목 데이터 (attributes 플랫 전개)
     */
    public function getItemByCode(string $code, bool $includeBom = false): array
    {
        $tenantId = $this->tenantId();

        // 1. Product에서 먼저 조회
        $query = Product::query()
            ->with('category:id,name')
            ->where('tenant_id', $tenantId)
            ->where('code', $code);

        if ($includeBom) {
            $query->with('componentLines.childProduct:id,code,name,unit');
        }

        $product = $query->first();

        if ($product) {
            $data = $this->flattenAttributes($product->toArray());
            $data['item_type'] = $product->product_type;
            $data['type_code'] = $product->product_type;

            return $data;
        }

        // 2. Material에서 조회
        $material = Material::query()
            ->with('category:id,name')
            ->where('tenant_id', $tenantId)
            ->where('material_code', $code)
            ->first();

        if ($material) {
            $data = $this->flattenAttributes($material->toArray());
            $data['item_type'] = $material->material_type;
            $data['code'] = $material->material_code;
            $data['type_code'] = $material->material_type;

            return $data;
        }

        throw new NotFoundHttpException(__('error.not_found'));
    }
}
