<?php

namespace App\Services\Products;

use App\Models\Products\Product;
use App\Models\Products\ProductComponent;
use App\Services\Service;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;

class ProductComponentResolver extends Service
{
    public function __construct(
        protected ?int $tenantId = null
    ) {
        // 주입값 없으면 Service::tenantId() 사용 (app('tenant_id')에서 끌어옴)
        $this->tenantId = $this->tenantId ?? $this->tenantId();
    }

    /** 테넌트별로 "제품처럼 자식이 있을 수 있는" ref_type 목록 */
    protected function productLikeTypes(): array
    {
        $all = config('products.product_like_types', []);
        $byTenant = Arr::get($all, (string) $this->tenantId, null);

        if (is_array($byTenant) && $byTenant) {
            return $byTenant;
        }

        return Arr::get($all, '*', ['PRODUCT']);
    }

    /** 한 부모 ID에 달린 컴포넌트 라인들 로드 (메모이즈) */
    protected function getLinesForParent(int $parentId): array
    {
        // 간단 메모이즈(요청 범위); 대량 호출 방지
        static $memo = [];
        if (array_key_exists($parentId, $memo)) {
            return $memo[$parentId];
        }

        $rows = ProductComponent::where('tenant_id', $this->tenantId)
            ->where('parent_product_id', $parentId)
            ->orderBy('sort_order')->orderBy('id')
            ->get([
                'id', 'tenant_id', 'parent_product_id',
                'category_id', 'category_name',
                'ref_type', 'ref_id', 'quantity', 'sort_order',
            ])
            ->map(fn ($r) => $r->getAttributes())  // ✅ 핵심 수정
            ->all();

        return $memo[$parentId] = $rows;
    }

    /** ref_type/ref_id 에 해당하는 노드의 "표시용 정보"를 로드 */
    protected function resolveNodeInfo(string $refType, int $refId): array
    {
        if ($refType === 'PRODUCT') {
            $p = Product::query()
                ->where('tenant_id', $this->tenantId)
                ->find($refId, ['id', 'code', 'name', 'product_type', 'category_id']);
            if (! $p) {
                return [
                    'id' => $refId,
                    'code' => null,
                    'name' => null,
                    'product_type' => null,
                    'category_id' => null,
                ];
            }

            return [
                'id' => (int) $p->id,
                'code' => $p->code,
                'name' => $p->name,
                'product_type' => $p->product_type,
                'category_id' => $p->category_id,
            ];
        }

        // ✅ MATERIAL 분기: materials 테이블 스키마 반영
        if ($refType === 'MATERIAL') {
            $m = DB::table('materials')
                ->where('tenant_id', $this->tenantId)
                ->where('id', $refId)
                ->whereNull('deleted_at') // 소프트 삭제 고려
                ->first([
                    'id',
                    'material_code',   // 코드
                    'item_name',       // 표시명(있으면 우선)
                    'name',            // fallback 표시명
                    'specification',   // 규격
                    'unit',
                    'category_id',
                ]);

            if (! $m) {
                return [
                    'id' => (int) $refId,
                    'code' => null,
                    'name' => null,
                    'unit' => null,
                    'category_id' => null,
                ];
            }

            // item_name 우선, 없으면 name 사용
            $displayName = $m->item_name ?: $m->name;

            return [
                'id' => (int) $m->id,
                'code' => $m->material_code,     // 표준 코드 필드
                'name' => $displayName,          // 사용자에게 보일 이름
                'unit' => $m->unit,
                'spec' => $m->specification,     // 있으면 프론트에서 활용 가능
                'category_id' => $m->category_id,
            ];
        }

        // 알 수 없는 타입 폴백
        return [
            'id' => $refId,
            'code' => null,
            'name' => null,
        ];
    }

    /**
     * 단일 제품을 루트로 트리를 생성 (재귀 / 사이클 방지 / 깊이 제한)
     *
     * @param  int  $productId  루트 제품 ID
     * @param  int|null  $maxDepth  최대 깊이(루트=0). null 이면 config default
     * @return array 트리 구조
     */
    public function resolveTree(int $productId, ?int $maxDepth = null): array
    {
        $maxDepth = $maxDepth ?? (int) config('products.default_tree_depth', 10);

        $root = Product::query()
            ->where('tenant_id', $this->tenantId)
            ->findOrFail($productId, ['id', 'code', 'name', 'product_type', 'category_id']);

        $visited = []; // 사이클 방지용 (product id 기준)

        $node = [
            'type' => 'PRODUCT',
            'id' => $root->id,
            'code' => $root->code,
            'name' => $root->name,
            'product_type' => $root->product_type,
            'category_id' => $root->category_id,
            'quantity' => 1,      // 루트는 수량 1로 간주
            'category' => null,   // 루트는 임의
            'children' => [],
            'depth' => 0,
        ];

        $node['children'] = $this->resolveChildren($root->id, 0, $maxDepth, $visited);

        return $node;
    }

    /**
     * 하위 노드(들) 재귀 확장
     *
     * @param  array  $visited  product-id 기준 사이클 방지
     */
    protected function resolveChildren(int $parentId, int $depth, int $maxDepth, array &$visited): array
    {
        // 깊이 제한
        if ($depth >= $maxDepth) {
            return [];
        }

        $lines = $this->getLinesForParent($parentId);
        if (! $lines) {
            return [];
        }

        $productLike = $this->productLikeTypes();
        $children = [];

        foreach ($lines as $line) {
            $refType = (string) $line['ref_type'];
            $refId = (int) $line['ref_id'];
            $qty = (float) $line['quantity'];

            if (! $refType || $refId <= 0) {
                // 로그 남기고 스킵
                // logger()->warning('Invalid component line', ['line' => $line]);
                continue;
            }

            $info = $this->resolveNodeInfo($refType, $refId);

            $child = [
                'type' => $refType,
                'id' => $info['id'] ?? $refId,
                'code' => $info['code'] ?? null,
                'name' => $info['name'] ?? null,
                'product_type' => $info['product_type'] ?? null,
                'category_id' => $info['category_id'] ?? null,
                'quantity' => $qty,
                'category' => [
                    'id' => $line['category_id'],
                    'name' => $line['category_name'],
                ],
                'sort_order' => (int) $line['sort_order'],
                'children' => [],
                'depth' => $depth + 1,
            ];

            // 제품처럼 자식이 달릴 수 있는 타입이면 재귀
            if (in_array($refType, $productLike, true)) {
                // 사이클 방지: 같은 product id 재방문 금지
                $pid = (int) $child['id'];
                if ($pid > 0) {
                    if (isset($visited[$pid])) {
                        $child['cycle'] = true; // 표식만 남기고 children 안탐
                    } else {
                        $visited[$pid] = true;
                        $child['children'] = $this->resolveChildren($pid, $depth + 1, $maxDepth, $visited);
                        unset($visited[$pid]); // 백트래킹
                    }
                }
            }

            $children[] = $child;
        }

        return $children;
    }
}
