<?php

namespace App\Http\Controllers\Api\V1;

use App\Helpers\ApiResponse;
use App\Http\Controllers\Controller;
use App\Models\Items\Item;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Items BOM Controller (ID-based)
 *
 * items.bom JSON 필드 기반 BOM 관리 컨트롤러
 * BOM 구조: [{child_item_id, quantity}, ...]
 */
class ItemsBomController extends Controller
{
    /**
     * GET /api/v1/items/bom
     * BOM이 있는 전체 품목 목록 조회 (item_id 없이)
     */
    public function listAll(Request $request)
    {
        return ApiResponse::handle(function () use ($request) {
            $tenantId = app('tenant_id');
            $perPage = (int) ($request->input('per_page', $request->input('size', 20)));
            $itemType = $request->input('item_type');

            $query = Item::query()
                ->where('tenant_id', $tenantId)
                ->whereNotNull('bom')
                ->where('bom', '!=', '[]')
                ->where('bom', '!=', 'null');

            // item_type 필터 (선택)
            if ($itemType) {
                $query->where('item_type', strtoupper($itemType));
            }

            $items = $query->orderBy('id')
                ->paginate($perPage, ['id', 'code', 'name', 'item_type', 'unit', 'bom']);

            // BOM 개수 추가
            $items->getCollection()->transform(function ($item) {
                $bom = $item->bom ?? [];

                return [
                    'id' => $item->id,
                    'code' => $item->code,
                    'name' => $item->name,
                    'item_type' => $item->item_type,
                    'unit' => $item->unit,
                    'bom_count' => count($bom),
                    'bom' => $this->expandBomItems($bom),
                ];
            });

            return $items;
        }, __('message.bom.fetch'));
    }

    /**
     * GET /api/v1/items/{id}/bom
     * BOM 라인 목록 조회 (flat list)
     */
    public function index(int $id, Request $request)
    {
        return ApiResponse::handle(function () use ($id) {
            $item = $this->getItem($id);
            $bom = $item->bom ?? [];

            // child_item 정보 확장
            return $this->expandBomItems($bom);
        }, __('message.bom.fetch'));
    }

    /**
     * GET /api/v1/items/{id}/bom/tree
     * BOM 트리 구조 조회 (계층적)
     */
    public function tree(int $id, Request $request)
    {
        return ApiResponse::handle(function () use ($id, $request) {
            $item = $this->getItem($id);
            $maxDepth = (int) ($request->input('depth', 3));

            return $this->buildBomTree($item, $maxDepth, 1);
        }, __('message.bom.fetch'));
    }

    /**
     * POST /api/v1/items/{id}/bom
     * BOM 라인 추가 (bulk upsert)
     */
    public function store(int $id, Request $request)
    {
        return ApiResponse::handle(function () use ($id, $request) {
            $item = $this->getItem($id);
            $inputItems = $request->input('items', []);
            $tenantId = app('tenant_id');

            // child_item_id 존재 검증
            $childIds = collect($inputItems)->pluck('child_item_id')->filter()->unique()->values()->toArray();
            if (! empty($childIds)) {
                $validIds = Item::where('tenant_id', $tenantId)
                    ->whereIn('id', $childIds)
                    ->pluck('id')
                    ->toArray();
                $invalidIds = array_diff($childIds, $validIds);
                if (! empty($invalidIds)) {
                    throw new \InvalidArgumentException(
                        __('error.bom.invalid_child_items', ['ids' => implode(', ', $invalidIds)])
                    );
                }
            }

            $existingBom = $item->bom ?? [];
            $existingMap = collect($existingBom)->keyBy('child_item_id')->toArray();

            // 입력된 items upsert
            foreach ($inputItems as $inputItem) {
                $childItemId = $inputItem['child_item_id'] ?? null;
                if (! $childItemId) {
                    continue;
                }

                // 자기 자신 참조 방지
                if ($childItemId == $id) {
                    continue;
                }

                $existingMap[$childItemId] = [
                    'child_item_id' => (int) $childItemId,
                    'quantity' => (float) ($inputItem['quantity'] ?? 1),
                ];
            }

            // 저장
            $item->bom = array_values($existingMap);
            $item->updated_by = auth()->id();
            $item->save();

            return $this->expandBomItems($item->bom);
        }, __('message.bom.created'));
    }

    /**
     * PUT /api/v1/items/{id}/bom/{lineId}
     * BOM 라인 수정 (lineId = child_item_id)
     */
    public function update(int $id, int $lineId, Request $request)
    {
        return ApiResponse::handle(function () use ($id, $lineId, $request) {
            $item = $this->getItem($id);
            $bom = $item->bom ?? [];

            $updated = false;
            foreach ($bom as &$entry) {
                if (($entry['child_item_id'] ?? null) == $lineId) {
                    if ($request->has('quantity')) {
                        $entry['quantity'] = (float) $request->input('quantity');
                    }
                    $updated = true;
                    break;
                }
            }

            if (! $updated) {
                throw new NotFoundHttpException(__('error.bom.line_not_found'));
            }

            $item->bom = $bom;
            $item->updated_by = auth()->id();
            $item->save();

            return $this->expandBomItems($item->bom);
        }, __('message.bom.updated'));
    }

    /**
     * DELETE /api/v1/items/{id}/bom/{lineId}
     * BOM 라인 삭제 (lineId = child_item_id)
     */
    public function destroy(int $id, int $lineId)
    {
        return ApiResponse::handle(function () use ($id, $lineId) {
            $item = $this->getItem($id);
            $bom = $item->bom ?? [];

            $originalCount = count($bom);
            $bom = array_values(array_filter($bom, function ($entry) use ($lineId) {
                return ($entry['child_item_id'] ?? null) != $lineId;
            }));

            if (count($bom) === $originalCount) {
                throw new NotFoundHttpException(__('error.bom.line_not_found'));
            }

            $item->bom = $bom;
            $item->updated_by = auth()->id();
            $item->save();

            return 'success';
        }, __('message.bom.deleted'));
    }

    /**
     * GET /api/v1/items/{id}/bom/summary
     * BOM 요약 정보
     */
    public function summary(int $id)
    {
        return ApiResponse::handle(function () use ($id) {
            $item = $this->getItem($id);
            $bom = $item->bom ?? [];

            $expandedBom = $this->expandBomItems($bom);

            return [
                'item_id' => $item->id,
                'item_code' => $item->code,
                'item_name' => $item->name,
                'total_lines' => count($bom),
                'total_quantity' => collect($bom)->sum('quantity'),
                'child_items' => collect($expandedBom)->pluck('child_item_name')->filter()->unique()->values(),
            ];
        }, __('message.bom.fetch'));
    }

    /**
     * GET /api/v1/items/{id}/bom/validate
     * BOM 유효성 검사
     */
    public function validate(int $id)
    {
        return ApiResponse::handle(function () use ($id) {
            $item = $this->getItem($id);
            $bom = $item->bom ?? [];
            $tenantId = app('tenant_id');

            $issues = [];

            // 1. 자기 자신 참조 체크
            foreach ($bom as $entry) {
                if (($entry['child_item_id'] ?? null) == $id) {
                    $issues[] = ['type' => 'self_reference', 'message' => '자기 자신을 BOM에 포함할 수 없습니다.'];
                }
            }

            // 2. 존재하지 않는 품목 체크
            $childIds = collect($bom)->pluck('child_item_id')->filter()->toArray();
            if (! empty($childIds)) {
                $existingIds = Item::where('tenant_id', $tenantId)
                    ->whereIn('id', $childIds)
                    ->pluck('id')
                    ->toArray();

                $missingIds = array_diff($childIds, $existingIds);
                foreach ($missingIds as $missingId) {
                    $issues[] = ['type' => 'missing_item', 'message' => "품목 ID {$missingId}가 존재하지 않습니다.", 'child_item_id' => $missingId];
                }
            }

            // 3. 순환 참조 체크 (1단계만)
            foreach ($childIds as $childId) {
                $childItem = Item::where('tenant_id', $tenantId)->find($childId);
                if ($childItem && ! empty($childItem->bom)) {
                    $grandchildIds = collect($childItem->bom)->pluck('child_item_id')->filter()->toArray();
                    if (in_array($id, $grandchildIds)) {
                        $issues[] = ['type' => 'circular_reference', 'message' => "순환 참조가 감지되었습니다: {$childItem->code}", 'child_item_id' => $childId];
                    }
                }
            }

            return [
                'is_valid' => empty($issues),
                'issues' => $issues,
            ];
        }, __('message.bom.fetch'));
    }

    /**
     * POST /api/v1/items/{id}/bom/replace
     * BOM 전체 교체
     */
    public function replace(int $id, Request $request)
    {
        return ApiResponse::handle(function () use ($id, $request) {
            $item = $this->getItem($id);
            $inputItems = $request->input('items', []);
            $tenantId = app('tenant_id');

            // child_item_id 존재 검증
            $childIds = collect($inputItems)->pluck('child_item_id')->filter()->unique()->values()->toArray();
            if (! empty($childIds)) {
                $validIds = Item::where('tenant_id', $tenantId)
                    ->whereIn('id', $childIds)
                    ->pluck('id')
                    ->toArray();
                $invalidIds = array_diff($childIds, $validIds);
                if (! empty($invalidIds)) {
                    throw new \InvalidArgumentException(
                        __('error.bom.invalid_child_items', ['ids' => implode(', ', $invalidIds)])
                    );
                }
            }

            $newBom = [];
            foreach ($inputItems as $inputItem) {
                $childItemId = $inputItem['child_item_id'] ?? null;
                if (! $childItemId || $childItemId == $id) {
                    continue;
                }

                $newBom[] = [
                    'child_item_id' => (int) $childItemId,
                    'quantity' => (float) ($inputItem['quantity'] ?? 1),
                ];
            }

            $item->bom = $newBom;
            $item->updated_by = auth()->id();
            $item->save();

            return $this->expandBomItems($item->bom);
        }, __('message.bom.created'));
    }

    /**
     * POST /api/v1/items/{id}/bom/reorder
     * BOM 정렬 변경
     */
    public function reorder(int $id, Request $request)
    {
        return ApiResponse::handle(function () use ($id, $request) {
            $item = $this->getItem($id);
            $orderedIds = $request->input('items', []);

            if (empty($orderedIds)) {
                return 'success';
            }

            $existingBom = $item->bom ?? [];
            $bomMap = collect($existingBom)->keyBy('child_item_id')->toArray();

            // 순서대로 재정렬
            $reorderedBom = [];
            foreach ($orderedIds as $childItemId) {
                if (isset($bomMap[$childItemId])) {
                    $reorderedBom[] = $bomMap[$childItemId];
                    unset($bomMap[$childItemId]);
                }
            }

            // 남은 항목 추가
            foreach ($bomMap as $entry) {
                $reorderedBom[] = $entry;
            }

            $item->bom = $reorderedBom;
            $item->updated_by = auth()->id();
            $item->save();

            return 'success';
        }, __('message.bom.reordered'));
    }

    /**
     * GET /api/v1/items/{id}/bom/categories
     * 해당 품목의 BOM에서 사용 중인 카테고리 목록
     */
    public function listCategories(int $id)
    {
        return ApiResponse::handle(function () use ($id) {
            $item = $this->getItem($id);
            $bom = $item->bom ?? [];
            $tenantId = app('tenant_id');

            $childIds = collect($bom)->pluck('child_item_id')->filter()->toArray();
            if (empty($childIds)) {
                return [];
            }

            $categories = Item::where('tenant_id', $tenantId)
                ->whereIn('id', $childIds)
                ->whereNotNull('category_id')
                ->with('category:id,name')
                ->get()
                ->pluck('category')
                ->filter()
                ->unique('id')
                ->values();

            return $categories;
        }, __('message.bom.fetch'));
    }

    // ==================== Helper Methods ====================

    /**
     * 품목 조회 및 tenant 소유권 검증
     *
     * @throws NotFoundHttpException
     */
    private function getItem(int $id): Item
    {
        $tenantId = app('tenant_id');

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

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

        return $item;
    }

    /**
     * BOM 항목에 child_item 상세 정보 확장
     */
    private function expandBomItems(array $bom): array
    {
        if (empty($bom)) {
            return [];
        }

        $tenantId = app('tenant_id');
        $childIds = collect($bom)->pluck('child_item_id')->filter()->toArray();

        $childItems = Item::where('tenant_id', $tenantId)
            ->whereIn('id', $childIds)
            ->get(['id', 'code', 'name', 'unit', 'item_type', 'category_id'])
            ->keyBy('id');

        return collect($bom)->map(function ($entry) use ($childItems) {
            $childItemId = $entry['child_item_id'] ?? null;
            $childItem = $childItems[$childItemId] ?? null;

            return [
                'child_item_id' => $childItemId,
                'child_item_code' => $childItem?->code,
                'child_item_name' => $childItem?->name,
                'child_item_type' => $childItem?->item_type,
                'unit' => $childItem?->unit,
                'quantity' => $entry['quantity'] ?? 1,
            ];
        })->toArray();
    }

    /**
     * BOM 트리 구조 빌드 (재귀)
     */
    private function buildBomTree(Item $item, int $maxDepth, int $currentDepth): array
    {
        $tenantId = app('tenant_id');
        $bom = $item->bom ?? [];

        $result = [
            'id' => $item->id,
            'code' => $item->code,
            'name' => $item->name,
            'item_type' => $item->item_type,
            'unit' => $item->unit,
            'depth' => $currentDepth,
            'children' => [],
        ];

        if (empty($bom) || $currentDepth >= $maxDepth) {
            return $result;
        }

        $childIds = collect($bom)->pluck('child_item_id')->filter()->toArray();
        $childItems = Item::where('tenant_id', $tenantId)
            ->whereIn('id', $childIds)
            ->get()
            ->keyBy('id');

        foreach ($bom as $entry) {
            $childItemId = $entry['child_item_id'] ?? null;
            $childItem = $childItems[$childItemId] ?? null;

            if ($childItem) {
                $childTree = $this->buildBomTree($childItem, $maxDepth, $currentDepth + 1);
                $childTree['quantity'] = $entry['quantity'] ?? 1;
                $result['children'][] = $childTree;
            }
        }

        return $result;
    }
}
