<?php

namespace App\Services;

use App\Models\Documents\Document;
use App\Models\Documents\DocumentData;
use App\Models\Documents\DocumentTemplate;
use App\Models\Orders\Order;
use App\Models\Orders\OrderItem;
use App\Models\Orders\OrderNode;
use App\Models\Qualitys\PerformanceReport;
use App\Models\Qualitys\QualityDocument;
use App\Models\Qualitys\QualityDocumentLocation;
use App\Models\Qualitys\QualityDocumentOrder;
use App\Services\Audit\AuditLogger;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class QualityDocumentService extends Service
{
    private const AUDIT_TARGET = 'quality_document';

    public function __construct(
        private readonly AuditLogger $auditLogger
    ) {}

    /**
     * 목록 조회
     */
    public function index(array $params)
    {
        $tenantId = $this->tenantId();
        $perPage = (int) ($params['per_page'] ?? 20);
        $q = trim((string) ($params['q'] ?? ''));
        $status = $params['status'] ?? null;
        $dateFrom = $params['date_from'] ?? null;
        $dateTo = $params['date_to'] ?? null;

        $query = QualityDocument::query()
            ->where('tenant_id', $tenantId)
            ->with(['client', 'inspector:id,name', 'creator:id,name', 'documentOrders.order', 'locations']);

        if ($q !== '') {
            $query->where(function ($qq) use ($q) {
                $qq->where('quality_doc_number', 'like', "%{$q}%")
                    ->orWhere('site_name', 'like', "%{$q}%");
            });
        }

        if ($status !== null) {
            $dbStatus = QualityDocument::mapStatusFromFrontend($status);
            $query->where('status', $dbStatus);
        }

        if ($dateFrom !== null) {
            $query->where('received_date', '>=', $dateFrom);
        }
        if ($dateTo !== null) {
            $query->where('received_date', '<=', $dateTo);
        }

        $query->orderByDesc('id');
        $paginated = $query->paginate($perPage);

        $transformedData = $paginated->getCollection()->map(fn ($doc) => $this->transformToFrontend($doc));

        return [
            'items' => $transformedData,
            'current_page' => $paginated->currentPage(),
            'last_page' => $paginated->lastPage(),
            'per_page' => $paginated->perPage(),
            'total' => $paginated->total(),
        ];
    }

    /**
     * 통계 조회
     */
    public function stats(array $params = []): array
    {
        $tenantId = $this->tenantId();

        $query = QualityDocument::where('tenant_id', $tenantId);

        if (! empty($params['date_from'])) {
            $query->where('received_date', '>=', $params['date_from']);
        }
        if (! empty($params['date_to'])) {
            $query->where('received_date', '<=', $params['date_to']);
        }

        $counts = (clone $query)
            ->select('status', DB::raw('count(*) as count'))
            ->groupBy('status')
            ->pluck('count', 'status')
            ->toArray();

        return [
            'reception_count' => $counts[QualityDocument::STATUS_RECEIVED] ?? 0,
            'in_progress_count' => $counts[QualityDocument::STATUS_IN_PROGRESS] ?? 0,
            'completed_count' => $counts[QualityDocument::STATUS_COMPLETED] ?? 0,
        ];
    }

    /**
     * 캘린더 스케줄 조회
     */
    public function calendar(array $params): array
    {
        $tenantId = $this->tenantId();
        $year = (int) ($params['year'] ?? now()->year);
        $month = (int) ($params['month'] ?? now()->month);

        $startDate = sprintf('%04d-%02d-01', $year, $month);
        $endDate = date('Y-m-t', strtotime($startDate));

        $query = QualityDocument::query()
            ->where('tenant_id', $tenantId)
            ->with(['inspector:id,name']);

        // options JSON 내 inspection.start_date / inspection.end_date 기준 필터링
        // received_date 기준으로 범위 필터 (options JSON은 직접 SQL 필터 어려우므로)
        $query->where(function ($q) use ($startDate, $endDate) {
            $q->whereBetween('received_date', [$startDate, $endDate]);
        });

        if (! empty($params['status'])) {
            $dbStatus = QualityDocument::mapStatusFromFrontend($params['status']);
            $query->where('status', $dbStatus);
        }

        return $query->orderBy('received_date')
            ->get()
            ->map(function (QualityDocument $doc) {
                $options = $doc->options ?? [];
                $inspection = $options['inspection'] ?? [];

                return [
                    'id' => $doc->id,
                    'start_date' => $inspection['start_date'] ?? $doc->received_date?->format('Y-m-d'),
                    'end_date' => $inspection['end_date'] ?? $doc->received_date?->format('Y-m-d'),
                    'inspector' => $doc->inspector?->name ?? '',
                    'site_name' => $doc->site_name,
                    'status' => QualityDocument::mapStatusToFrontend($doc->status),
                ];
            })
            ->values()
            ->toArray();
    }

    /**
     * 단건 조회
     */
    public function show(int $id)
    {
        $tenantId = $this->tenantId();

        $doc = QualityDocument::where('tenant_id', $tenantId)
            ->with([
                'client',
                'inspector:id,name',
                'creator:id,name',
                'documentOrders.order',
                'locations.orderItem.node',
            ])
            ->find($id);

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

        return $this->transformToFrontend($doc, true);
    }

    /**
     * 생성
     */
    public function store(array $data)
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($data, $tenantId, $userId) {
            // order_ids는 별도 처리 후 $data에서 제거
            $orderIds = $data['order_ids'] ?? null;
            unset($data['order_ids']);

            $data['tenant_id'] = $tenantId;
            $data['quality_doc_number'] = QualityDocument::generateDocNumber($tenantId);
            $data['status'] = QualityDocument::STATUS_RECEIVED;
            $data['created_by'] = $userId;

            $doc = QualityDocument::create($data);

            // 수주 연결
            if (! empty($orderIds)) {
                $this->syncOrders($doc, $orderIds, $tenantId);
            }

            $this->auditLogger->log(
                $tenantId,
                self::AUDIT_TARGET,
                $doc->id,
                'created',
                null,
                $doc->toArray()
            );

            $doc->load(['client', 'inspector:id,name', 'creator:id,name', 'documentOrders.order', 'locations.orderItem.node']);

            // 요청서 Document(EAV) 자동생성
            $this->syncRequestDocument($doc);

            return $this->transformToFrontend($doc);
        });
    }

    /**
     * 수정
     */
    public function update(int $id, array $data)
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        $doc = QualityDocument::where('tenant_id', $tenantId)->find($id);
        if (! $doc) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        $beforeData = $doc->toArray();

        return DB::transaction(function () use ($doc, $data, $userId, $beforeData, $tenantId) {
            // order_ids, locations는 별도 처리 후 $data에서 제거
            $orderIds = $data['order_ids'] ?? null;
            $locations = $data['locations'] ?? null;
            unset($data['order_ids'], $data['locations']);

            $data['updated_by'] = $userId;

            // options는 기존 값과 병합
            if (isset($data['options'])) {
                $existingOptions = $doc->options ?? [];
                $data['options'] = array_replace_recursive($existingOptions, $data['options']);
            }

            $doc->update($data);

            // 수주 동기화 (order_ids가 전달된 경우만)
            if ($orderIds !== null) {
                $this->syncOrders($doc, $orderIds, $tenantId);
            }

            // 개소별 데이터 업데이트 (시공규격, 변경사유, 검사데이터)
            if (! empty($locations)) {
                $this->updateLocations($doc->id, $locations);
            }

            // 개소 상태 기반 문서 상태 재계산
            $this->recalculateDocumentStatus($doc);

            $this->auditLogger->log(
                $doc->tenant_id,
                self::AUDIT_TARGET,
                $doc->id,
                'updated',
                $beforeData,
                $doc->fresh()->toArray()
            );

            $doc->load(['client', 'inspector:id,name', 'creator:id,name', 'documentOrders.order', 'locations.orderItem.node']);

            // 요청서 Document(EAV) 동기화
            $this->syncRequestDocument($doc);

            return $this->transformToFrontend($doc);
        });
    }

    /**
     * 삭제
     */
    public function destroy(int $id)
    {
        $tenantId = $this->tenantId();

        $doc = QualityDocument::where('tenant_id', $tenantId)->find($id);
        if (! $doc) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        if ($doc->isCompleted()) {
            throw new BadRequestHttpException(__('error.quality.cannot_delete_completed'));
        }

        $beforeData = $doc->toArray();
        $doc->deleted_by = $this->apiUserId();
        $doc->save();
        $doc->delete();

        $this->auditLogger->log(
            $tenantId,
            self::AUDIT_TARGET,
            $doc->id,
            'deleted',
            $beforeData,
            null
        );

        return 'success';
    }

    /**
     * 검사 완료 처리
     */
    public function complete(int $id)
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        $doc = QualityDocument::where('tenant_id', $tenantId)
            ->with(['locations'])
            ->find($id);

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

        if ($doc->isCompleted()) {
            throw new BadRequestHttpException(__('error.quality.already_completed'));
        }

        // 미완료 개소 확인
        $pendingCount = $doc->locations->where('inspection_status', QualityDocumentLocation::STATUS_PENDING)->count();
        if ($pendingCount > 0) {
            throw new BadRequestHttpException(__('error.quality.pending_locations', ['count' => $pendingCount]));
        }

        $beforeData = $doc->toArray();

        return DB::transaction(function () use ($doc, $userId, $tenantId, $beforeData) {
            $doc->update([
                'status' => QualityDocument::STATUS_COMPLETED,
                'updated_by' => $userId,
            ]);

            // 실적신고 자동 생성
            $now = now();
            PerformanceReport::firstOrCreate(
                [
                    'tenant_id' => $tenantId,
                    'quality_document_id' => $doc->id,
                ],
                [
                    'year' => $now->year,
                    'quarter' => (int) ceil($now->month / 3),
                    'confirmation_status' => PerformanceReport::STATUS_UNCONFIRMED,
                    'created_by' => $userId,
                ]
            );

            $this->auditLogger->log(
                $tenantId,
                self::AUDIT_TARGET,
                $doc->id,
                'completed',
                $beforeData,
                $doc->fresh()->toArray()
            );

            return $this->transformToFrontend($doc->load(['client', 'inspector:id,name', 'creator:id,name']));
        });
    }

    /**
     * 검사 미등록 수주 목록
     */
    public function availableOrders(array $params): array
    {
        $tenantId = $this->tenantId();
        $q = trim((string) ($params['q'] ?? ''));
        $clientId = $params['client_id'] ?? null;
        $itemId = $params['item_id'] ?? null;

        // 이미 연결된 수주 ID 목록
        $linkedOrderIds = QualityDocumentOrder::whereHas('qualityDocument', function ($query) use ($tenantId) {
            $query->where('tenant_id', $tenantId);
        })->pluck('order_id');

        $query = Order::where('tenant_id', $tenantId)
            ->whereNotIn('id', $linkedOrderIds)
            ->with(['item:id,name', 'nodes' => function ($q) {
                $q->whereNull('parent_id')->orderBy('sort_order')
                    ->with(['items' => function ($q2) {
                        $q2->orderBy('sort_order')->limit(1);
                    }]);
            }])
            ->withCount(['nodes as location_count' => function ($q) {
                $q->whereNull('parent_id');
            }]);

        if ($q !== '') {
            $query->where(function ($qq) use ($q) {
                $qq->where('order_no', 'like', "%{$q}%")
                    ->orWhere('site_name', 'like', "%{$q}%");
            });
        }

        // 같은 거래처(발주처) 필터
        if ($clientId) {
            $query->where('client_id', $clientId);
        }

        // 같은 모델(품목) 필터
        if ($itemId) {
            $query->where('item_id', $itemId);
        }

        return $query->orderByDesc('id')
            ->limit(50)
            ->get()
            ->map(fn ($order) => [
                'id' => $order->id,
                'order_number' => $order->order_no,
                'site_name' => $order->site_name ?? '',
                'client_id' => $order->client_id,
                'client_name' => $order->client_name ?? '',
                'item_id' => $order->item_id,
                'item_name' => $order->item?->name ?? '',
                'delivery_date' => $order->delivery_date?->format('Y-m-d') ?? '',
                'location_count' => $order->location_count,
                'locations' => $order->nodes->where('parent_id', null)->map(function ($node) {
                    $item = $node->items->first();
                    $options = $node->options ?? [];

                    return [
                        'node_id' => $node->id,
                        'floor' => $item?->floor_code ?? $node->code ?? '',
                        'symbol' => $item?->symbol_code ?? '',
                        'order_width' => $options['width'] ?? 0,
                        'order_height' => $options['height'] ?? 0,
                    ];
                })->values()->toArray(),
            ])
            ->toArray();
    }

    /**
     * 개소별 데이터 업데이트
     */
    private function updateLocations(int $docId, array $locations): void
    {
        foreach ($locations as $locData) {
            $locId = $locData['id'] ?? null;
            if (! $locId) {
                continue;
            }

            $location = QualityDocumentLocation::where('quality_document_id', $docId)->find($locId);
            if (! $location) {
                continue;
            }

            $updateData = [];
            if (array_key_exists('post_width', $locData)) {
                $updateData['post_width'] = $locData['post_width'];
            }
            if (array_key_exists('post_height', $locData)) {
                $updateData['post_height'] = $locData['post_height'];
            }
            if (array_key_exists('change_reason', $locData)) {
                $updateData['change_reason'] = $locData['change_reason'];
            }
            if (array_key_exists('inspection_data', $locData)) {
                $updateData['inspection_data'] = $locData['inspection_data'];
            }

            if (! empty($updateData)) {
                $location->update($updateData);
            }

            // 검사 데이터 내용 기반 inspection_status 재계산
            $location->refresh();
            $newStatus = $this->determineLocationStatus($location->inspection_data);

            if ($location->inspection_status !== $newStatus) {
                $location->update(['inspection_status' => $newStatus]);
            }
        }
    }

    /**
     * 개소 상태 기반 문서 상태 재계산
     */
    private function recalculateDocumentStatus(QualityDocument $doc): void
    {
        $doc->load('locations');
        $total = $doc->locations->count();

        if ($total === 0) {
            $doc->update(['status' => QualityDocument::STATUS_RECEIVED]);

            return;
        }

        $completedCount = $doc->locations
            ->where('inspection_status', QualityDocumentLocation::STATUS_COMPLETED)
            ->count();
        $inProgressCount = $doc->locations
            ->where('inspection_status', QualityDocumentLocation::STATUS_IN_PROGRESS)
            ->count();

        if ($completedCount === $total) {
            $doc->update(['status' => QualityDocument::STATUS_COMPLETED]);
        } elseif ($completedCount > 0 || $inProgressCount > 0) {
            $doc->update(['status' => QualityDocument::STATUS_IN_PROGRESS]);
        } else {
            $doc->update(['status' => QualityDocument::STATUS_RECEIVED]);
        }
    }

    /**
     * 검사 데이터 내용 기반 개소 상태 판정
     *
     * - 데이터 없음 or 검사항목 0개+사진 없음 → pending
     * - 검사항목 일부 or 사진 없음 → in_progress
     * - 15개 검사항목 전부 + 사진 있음 → completed
     */
    private function determineLocationStatus(?array $inspectionData): string
    {
        if (empty($inspectionData)) {
            return QualityDocumentLocation::STATUS_PENDING;
        }

        $judgmentFields = [
            'appearanceProcessing', 'appearanceSewing', 'appearanceAssembly',
            'appearanceSmokeBarrier', 'appearanceBottomFinish', 'motor', 'material',
            'lengthJudgment', 'heightJudgment', 'guideRailGap', 'bottomFinishGap',
            'fireResistanceTest', 'smokeLeakageTest', 'openCloseTest', 'impactTest',
        ];

        $inspected = 0;
        foreach ($judgmentFields as $field) {
            if (isset($inspectionData[$field]) && $inspectionData[$field] !== null && $inspectionData[$field] !== '') {
                $inspected++;
            }
        }

        $hasPhotos = ! empty($inspectionData['productImages']) && is_array($inspectionData['productImages']) && count($inspectionData['productImages']) > 0;

        if ($inspected === 0 && ! $hasPhotos) {
            return QualityDocumentLocation::STATUS_PENDING;
        }

        if ($inspected < count($judgmentFields) || ! $hasPhotos) {
            return QualityDocumentLocation::STATUS_IN_PROGRESS;
        }

        return QualityDocumentLocation::STATUS_COMPLETED;
    }

    /**
     * 수주 동기화 (update 시 사용)
     */
    private function syncOrders(QualityDocument $doc, array $orderIds, int $tenantId): void
    {
        $existingOrderIds = QualityDocumentOrder::where('quality_document_id', $doc->id)
            ->pluck('order_id')
            ->toArray();

        $toAttach = array_diff($orderIds, $existingOrderIds);
        $toDetach = array_diff($existingOrderIds, $orderIds);

        // 새로 연결
        foreach ($toAttach as $orderId) {
            $order = Order::where('tenant_id', $tenantId)->find($orderId);
            if (! $order) {
                continue;
            }

            $docOrder = QualityDocumentOrder::firstOrCreate([
                'quality_document_id' => $doc->id,
                'order_id' => $orderId,
            ]);

            // 개소(root OrderNode) 기준으로 location 생성
            $rootNodes = OrderNode::where('order_id', $orderId)
                ->whereNull('parent_id')
                ->orderBy('sort_order')
                ->get();

            foreach ($rootNodes as $node) {
                // 각 개소의 대표 OrderItem (해당 노드 하위 첫 번째 품목)
                $representativeItem = OrderItem::where('order_node_id', $node->id)
                    ->orderBy('sort_order')
                    ->first();

                if ($representativeItem) {
                    QualityDocumentLocation::firstOrCreate([
                        'quality_document_id' => $doc->id,
                        'quality_document_order_id' => $docOrder->id,
                        'order_item_id' => $representativeItem->id,
                    ]);
                }
            }
        }

        // 연결 해제
        foreach ($toDetach as $orderId) {
            $docOrder = QualityDocumentOrder::where('quality_document_id', $doc->id)
                ->where('order_id', $orderId)
                ->first();

            if ($docOrder) {
                QualityDocumentLocation::where('quality_document_order_id', $docOrder->id)->delete();
                $docOrder->delete();
            }
        }
    }

    /**
     * 수주 연결
     */
    public function attachOrders(int $docId, array $orderIds)
    {
        $tenantId = $this->tenantId();

        $doc = QualityDocument::where('tenant_id', $tenantId)->find($docId);
        if (! $doc) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        return DB::transaction(function () use ($doc, $orderIds, $tenantId) {
            foreach ($orderIds as $orderId) {
                $order = Order::where('tenant_id', $tenantId)->find($orderId);
                if (! $order) {
                    continue;
                }

                // 중복 체크
                $docOrder = QualityDocumentOrder::firstOrCreate([
                    'quality_document_id' => $doc->id,
                    'order_id' => $orderId,
                ]);

                // 수주 연결 시 개소(root OrderNode)를 locations에 자동 생성
                $rootNodes = OrderNode::where('order_id', $orderId)
                    ->whereNull('parent_id')
                    ->orderBy('sort_order')
                    ->get();

                foreach ($rootNodes as $node) {
                    $representativeItem = OrderItem::where('order_node_id', $node->id)
                        ->orderBy('sort_order')
                        ->first();

                    if ($representativeItem) {
                        QualityDocumentLocation::firstOrCreate([
                            'quality_document_id' => $doc->id,
                            'quality_document_order_id' => $docOrder->id,
                            'order_item_id' => $representativeItem->id,
                        ]);
                    }
                }
            }

            // 상태를 진행중으로 변경 (접수 상태일 때)
            if ($doc->isReceived()) {
                $doc->update(['status' => QualityDocument::STATUS_IN_PROGRESS]);
            }

            $doc->load(['client', 'inspector:id,name', 'creator:id,name', 'documentOrders.order', 'locations.orderItem.node']);

            // 요청서 Document(EAV) 동기화 (개소 추가됨)
            $this->syncRequestDocument($doc);

            return $this->transformToFrontend($doc);
        });
    }

    /**
     * 수주 연결 해제
     */
    public function detachOrder(int $docId, int $orderId)
    {
        $tenantId = $this->tenantId();

        $doc = QualityDocument::where('tenant_id', $tenantId)->find($docId);
        if (! $doc) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        if ($doc->isCompleted()) {
            throw new BadRequestHttpException(__('error.quality.cannot_modify_completed'));
        }

        $docOrder = QualityDocumentOrder::where('quality_document_id', $docId)
            ->where('order_id', $orderId)
            ->first();

        if ($docOrder) {
            // 해당 수주의 locations 삭제
            QualityDocumentLocation::where('quality_document_order_id', $docOrder->id)->delete();
            $docOrder->delete();
        }

        return $this->transformToFrontend(
            $doc->load(['client', 'inspector:id,name', 'creator:id,name', 'documentOrders.order', 'locations.orderItem.node'])
        );
    }

    /**
     * 필수정보 계산
     */
    public function calculateRequiredInfo(QualityDocument $doc): string
    {
        $options = $doc->options ?? [];
        $missing = 0;

        $sections = [
            'construction_site' => ['name', 'land_location', 'lot_number'],
            'material_distributor' => ['company', 'address', 'ceo', 'phone'],
            'contractor' => ['company', 'address', 'name', 'phone'],
            'supervisor' => ['office', 'address', 'name', 'phone'],
        ];

        foreach ($sections as $section => $fields) {
            $data = $options[$section] ?? [];
            foreach ($fields as $field) {
                if (empty($data[$field])) {
                    $missing++;
                    break; // 섹션 단위
                }
            }
        }

        return $missing === 0 ? '완료' : "{$missing}건 누락";
    }

    /**
     * DB → 프론트엔드 변환
     */
    private function transformToFrontend(QualityDocument $doc, bool $detail = false): array
    {
        $options = $doc->options ?? [];

        $result = [
            'id' => $doc->id,
            'quality_doc_number' => $doc->quality_doc_number,
            'site_name' => $doc->site_name,
            'client_id' => $doc->client_id,
            'client' => $doc->client?->name ?? '',
            'location_count' => $doc->locations?->count() ?? 0,
            'required_info' => $this->calculateRequiredInfo($doc),
            'inspection_period' => $this->formatInspectionPeriod($options),
            'inspector' => $doc->inspector?->name ?? '',
            'status' => QualityDocument::mapStatusToFrontend($doc->status),
            'author' => $doc->creator?->name ?? '',
            'reception_date' => $doc->received_date?->format('Y-m-d'),
            'manager' => $options['manager']['name'] ?? '',
            'manager_contact' => $options['manager']['phone'] ?? '',
        ];

        // 요청서 Document ID (EAV)
        $requestDoc = Document::where('tenant_id', $doc->tenant_id)
            ->where('template_id', self::REQUEST_TEMPLATE_ID)
            ->where('linkable_type', QualityDocument::class)
            ->where('linkable_id', $doc->id)
            ->first();
        $result['request_document_id'] = $requestDoc?->id;

        if ($detail) {
            $result['construction_site'] = [
                'site_name' => $options['construction_site']['name'] ?? '',
                'land_location' => $options['construction_site']['land_location'] ?? '',
                'lot_number' => $options['construction_site']['lot_number'] ?? '',
            ];
            $result['material_distributor'] = [
                'company_name' => $options['material_distributor']['company'] ?? '',
                'company_address' => $options['material_distributor']['address'] ?? '',
                'representative_name' => $options['material_distributor']['ceo'] ?? '',
                'phone' => $options['material_distributor']['phone'] ?? '',
            ];
            $result['constructor_info'] = [
                'company_name' => $options['contractor']['company'] ?? '',
                'company_address' => $options['contractor']['address'] ?? '',
                'name' => $options['contractor']['name'] ?? '',
                'phone' => $options['contractor']['phone'] ?? '',
            ];
            $result['supervisor'] = [
                'office_name' => $options['supervisor']['office'] ?? '',
                'office_address' => $options['supervisor']['address'] ?? '',
                'name' => $options['supervisor']['name'] ?? '',
                'phone' => $options['supervisor']['phone'] ?? '',
            ];
            $result['schedule_info'] = [
                'visit_request_date' => $options['inspection']['request_date'] ?? '',
                'start_date' => $options['inspection']['start_date'] ?? '',
                'end_date' => $options['inspection']['end_date'] ?? '',
                'inspector' => $doc->inspector?->name ?? '',
                'site_postal_code' => $options['site_address']['postal_code'] ?? '',
                'site_address' => $options['site_address']['address'] ?? '',
                'site_address_detail' => $options['site_address']['detail'] ?? '',
            ];

            // 개소 목록 (각 location은 1개 root OrderNode = 1개 개소)
            $result['order_items'] = $doc->locations->map(function ($loc) {
                $orderItem = $loc->orderItem;
                $node = $orderItem?->node;
                $nodeOptions = $node?->options ?? [];
                $order = $loc->qualityDocumentOrder?->order;

                return [
                    'id' => (string) $loc->id,
                    'order_id' => $order?->id,
                    'order_number' => $order?->order_no ?? '',
                    'site_name' => $order?->site_name ?? '',
                    'client_id' => $order?->client_id,
                    'client_name' => $order?->client_name ?? '',
                    'item_id' => $order?->item_id,
                    'item_name' => $order?->item?->name ?? '',
                    'delivery_date' => $order?->delivery_date ? $order->delivery_date->format('Y-m-d') : '',
                    'floor' => $orderItem?->floor_code ?? '',
                    'symbol' => $orderItem?->symbol_code ?? '',
                    'order_width' => $nodeOptions['width'] ?? 0,
                    'order_height' => $nodeOptions['height'] ?? 0,
                    'construction_width' => $loc->post_width ?? 0,
                    'construction_height' => $loc->post_height ?? 0,
                    'change_reason' => $loc->change_reason ?? '',
                    'inspection_data' => $loc->inspection_data,
                    'document_id' => $loc->document_id,
                ];
            })->toArray();
        }

        return $result;
    }

    /**
     * 개소별 검사 저장 (시공후 규격 + 검사 성적서)
     */
    public function inspectLocation(int $docId, int $locId, array $data)
    {
        $tenantId = $this->tenantId();

        $doc = QualityDocument::where('tenant_id', $tenantId)->find($docId);
        if (! $doc) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        $location = QualityDocumentLocation::where('quality_document_id', $docId)->find($locId);
        if (! $location) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        return DB::transaction(function () use ($location, $data, $doc) {
            $updateData = [];

            if (isset($data['post_width'])) {
                $updateData['post_width'] = $data['post_width'];
            }
            if (isset($data['post_height'])) {
                $updateData['post_height'] = $data['post_height'];
            }
            if (isset($data['change_reason'])) {
                $updateData['change_reason'] = $data['change_reason'];
            }
            if (array_key_exists('inspection_data', $data)) {
                $updateData['inspection_data'] = $data['inspection_data'];
            }

            if (! empty($updateData)) {
                $location->update($updateData);
            }

            // 검사 데이터 기반 개소 상태 자동 판정
            $location->refresh();
            $newLocStatus = $this->determineLocationStatus($location->inspection_data);
            if ($location->inspection_status !== $newLocStatus) {
                $location->update(['inspection_status' => $newLocStatus]);
            }

            // 문서 상태 재계산
            $this->recalculateDocumentStatus($doc);

            return $location->fresh()->toArray();
        });
    }

    /**
     * 검사제품요청서 데이터 (PDF/프린트용)
     */
    public function requestDocument(int $id): array
    {
        $tenantId = $this->tenantId();

        $doc = QualityDocument::where('tenant_id', $tenantId)
            ->with([
                'client',
                'inspector:id,name',
                'documentOrders.order',
                'locations.orderItem.node',
            ])
            ->find($id);

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

        $options = $doc->options ?? [];

        return [
            'quality_doc_number' => $doc->quality_doc_number,
            'site_name' => $doc->site_name,
            'client' => $doc->client?->name ?? '',
            'received_date' => $doc->received_date?->format('Y-m-d'),
            'inspector' => $doc->inspector?->name ?? '',
            'construction_site' => $options['construction_site'] ?? [],
            'material_distributor' => $options['material_distributor'] ?? [],
            'contractor' => $options['contractor'] ?? [],
            'supervisor' => $options['supervisor'] ?? [],
            'inspection' => $options['inspection'] ?? [],
            'site_address' => $options['site_address'] ?? [],
            'manager' => $options['manager'] ?? [],
            'items' => $doc->locations->map(function ($loc) {
                $orderItem = $loc->orderItem;
                $node = $orderItem?->node;
                $nodeOptions = $node?->options ?? [];
                $order = $loc->qualityDocumentOrder?->order;

                return [
                    'order_number' => $order?->order_no ?? '',
                    'floor' => $orderItem?->floor_code ?? '',
                    'symbol' => $orderItem?->symbol_code ?? '',
                    'item_name' => $orderItem?->item_name ?? '',
                    'specification' => $orderItem?->specification ?? '',
                    'order_width' => $nodeOptions['width'] ?? 0,
                    'order_height' => $nodeOptions['height'] ?? 0,
                    'quantity' => $orderItem?->quantity ?? 1,
                ];
            })->toArray(),
        ];
    }

    /**
     * 제품검사성적서 데이터 (documents EAV 연동)
     */
    public function resultDocument(int $id): array
    {
        $tenantId = $this->tenantId();

        $doc = QualityDocument::where('tenant_id', $tenantId)
            ->with([
                'client',
                'inspector:id,name',
                'locations.orderItem.node',
                'locations.document.data',
                'locations.document.template',
            ])
            ->find($id);

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

        $options = $doc->options ?? [];

        return [
            'quality_doc_number' => $doc->quality_doc_number,
            'site_name' => $doc->site_name,
            'client' => $doc->client?->name ?? '',
            'inspector' => $doc->inspector?->name ?? '',
            'status' => QualityDocument::mapStatusToFrontend($doc->status),
            'locations' => $doc->locations->map(function ($loc) {
                $orderItem = $loc->orderItem;
                $node = $orderItem?->node;
                $nodeOptions = $node?->options ?? [];
                $document = $loc->document;

                $result = [
                    'id' => $loc->id,
                    'floor' => $orderItem?->floor_code ?? '',
                    'symbol' => $orderItem?->symbol_code ?? '',
                    'order_width' => $nodeOptions['width'] ?? 0,
                    'order_height' => $nodeOptions['height'] ?? 0,
                    'post_width' => $loc->post_width,
                    'post_height' => $loc->post_height,
                    'change_reason' => $loc->change_reason,
                    'inspection_status' => $loc->inspection_status,
                    'document_id' => $loc->document_id,
                ];

                // EAV 문서 데이터가 있으면 포함
                if ($document) {
                    $result['document'] = [
                        'id' => $document->id,
                        'document_no' => $document->document_no,
                        'status' => $document->status,
                        'template_id' => $document->template_id,
                        'data' => $document->data?->map(fn ($d) => [
                            'field_key' => $d->field_key,
                            'field_value' => $d->field_value,
                            'section_id' => $d->section_id,
                            'column_id' => $d->column_id,
                        ])->toArray() ?? [],
                    ];
                }

                return $result;
            })->toArray(),
        ];
    }

    // =========================================================================
    // 제품검사 요청서 Document 자동생성/동기화
    // =========================================================================

    private const REQUEST_TEMPLATE_ID = 66;

    /**
     * 요청서 Document(EAV) 동기화
     *
     * quality_document 생성/수정 시 호출.
     * - Document 없으면 생성 (template_id=66, linkable=QualityDocument)
     * - 기본필드 + 섹션 데이터 + 사전고지 테이블을 EAV로 매핑
     */
    private function syncRequestDocument(QualityDocument $doc): void
    {
        $tenantId = $doc->tenant_id;

        // 템플릿 존재 확인
        $template = DocumentTemplate::where('tenant_id', $tenantId)
            ->where('id', self::REQUEST_TEMPLATE_ID)
            ->with(['basicFields', 'sections.items', 'columns'])
            ->first();

        if (! $template) {
            return; // 템플릿 미등록 시 스킵
        }

        // 기존 Document 조회 또는 생성
        $document = Document::where('tenant_id', $tenantId)
            ->where('template_id', self::REQUEST_TEMPLATE_ID)
            ->where('linkable_type', QualityDocument::class)
            ->where('linkable_id', $doc->id)
            ->first();

        if (! $document) {
            $documentNo = $this->generateRequestDocumentNo($tenantId);
            $document = Document::create([
                'tenant_id' => $tenantId,
                'template_id' => self::REQUEST_TEMPLATE_ID,
                'document_no' => $documentNo,
                'title' => '제품검사 요청서 - '.($doc->site_name ?? $doc->quality_doc_number),
                'status' => Document::STATUS_DRAFT,
                'linkable_type' => QualityDocument::class,
                'linkable_id' => $doc->id,
                'created_by' => $doc->created_by,
                'updated_by' => $doc->updated_by ?? $doc->created_by,
            ]);
        } else {
            // rendered_html 초기화 (데이터 변경 시 재캡처 필요)
            $document->update([
                'rendered_html' => null,
                'updated_by' => $doc->updated_by ?? $doc->created_by,
            ]);
        }

        // 기존 EAV 데이터 삭제 후 재생성
        DocumentData::where('document_id', $document->id)->delete();

        $options = $doc->options ?? [];
        $eavData = [];

        // 1. 기본필드 매핑 (quality_document → basicFields)
        $fieldMapping = $this->buildBasicFieldMapping($doc, $options);
        foreach ($template->basicFields as $bf) {
            $value = $fieldMapping[$bf->field_key] ?? '';
            if ($value !== '') {
                $eavData[] = [
                    'document_id' => $document->id,
                    'section_id' => null,
                    'column_id' => null,
                    'row_index' => 0,
                    'field_key' => $bf->field_key,
                    'field_value' => (string) $value,
                ];
            }
        }

        // 2. 섹션 아이템 매핑 (options → section items)
        $sectionMapping = $this->buildSectionMapping($options);
        foreach ($template->sections as $section) {
            if ($section->items->isEmpty()) {
                continue; // 사전 고지 정보 섹션은 items가 없으므로 스킵
            }
            $sectionData = $sectionMapping[$section->title] ?? [];
            foreach ($section->items as $item) {
                $value = $sectionData[$item->item] ?? '';
                if ($value !== '') {
                    $eavData[] = [
                        'document_id' => $document->id,
                        'section_id' => $section->id,
                        'column_id' => null,
                        'row_index' => 0,
                        'field_key' => $item->item, // item name as key
                        'field_value' => (string) $value,
                    ];
                }
            }
        }

        // 3. 사전고지 테이블 매핑 (locations → columns)
        $doc->loadMissing(['locations.orderItem.node', 'locations.qualityDocumentOrder.order']);
        $columns = $template->columns->sortBy('sort_order');

        foreach ($doc->locations as $rowIdx => $loc) {
            $orderItem = $loc->orderItem;
            $node = $orderItem?->node;
            $nodeOptions = $node?->options ?? [];
            $order = $loc->qualityDocumentOrder?->order;

            $rowData = [
                'No.' => (string) ($rowIdx + 1),
                '층수' => $orderItem?->floor_code ?? '',
                '부호' => $orderItem?->symbol_code ?? '',
                '발주 가로' => (string) ($nodeOptions['width'] ?? ''),
                '발주 세로' => (string) ($nodeOptions['height'] ?? ''),
                '시공 가로' => (string) ($loc->post_width ?? ''),
                '시공 세로' => (string) ($loc->post_height ?? ''),
                '변경사유' => $loc->change_reason ?? '',
            ];

            foreach ($columns as $col) {
                $value = $rowData[$col->label] ?? '';
                $eavData[] = [
                    'document_id' => $document->id,
                    'section_id' => null,
                    'column_id' => $col->id,
                    'row_index' => $rowIdx,
                    'field_key' => $col->label,
                    'field_value' => $value,
                ];
            }
        }

        // EAV 일괄 삽입
        if (! empty($eavData)) {
            DocumentData::insert(array_map(function ($d) {
                $d['created_at'] = now();
                $d['updated_at'] = now();

                return $d;
            }, $eavData));
        }
    }

    /**
     * 기본필드 매핑 (quality_document → template basicFields)
     */
    private function buildBasicFieldMapping(QualityDocument $doc, array $options): array
    {
        $manager = $options['manager'] ?? [];
        $inspection = $options['inspection'] ?? [];
        $siteAddress = $options['site_address'] ?? [];
        $order = $doc->documentOrders?->first()?->order;

        return [
            'client' => $doc->client?->name ?? '',
            'company_name' => $manager['company'] ?? '',
            'manager' => $manager['name'] ?? '',
            'order_number' => $order?->order_no ?? '',
            'manager_contact' => $manager['phone'] ?? '',
            'site_name' => $doc->site_name ?? '',
            'delivery_date' => $order?->delivery_date?->format('Y-m-d') ?? '',
            'site_address' => trim(($siteAddress['address'] ?? '').' '.($siteAddress['detail'] ?? '')),
            'total_locations' => (string) ($doc->locations?->count() ?? 0),
            'receipt_date' => $doc->received_date?->format('Y-m-d') ?? '',
            'inspection_request_date' => $inspection['request_date'] ?? '',
        ];
    }

    /**
     * 섹션 데이터 매핑 (options → section items by section title)
     */
    private function buildSectionMapping(array $options): array
    {
        $cs = $options['construction_site'] ?? [];
        $md = $options['material_distributor'] ?? [];
        $ct = $options['contractor'] ?? [];
        $sv = $options['supervisor'] ?? [];

        return [
            '건축공사장 정보' => [
                '현장명' => $cs['name'] ?? '',
                '대지위치' => $cs['land_location'] ?? '',
                '지번' => $cs['lot_number'] ?? '',
            ],
            '자재유통업자 정보' => [
                '회사명' => $md['company'] ?? '',
                '주소' => $md['address'] ?? '',
                '대표자' => $md['ceo'] ?? '',
                '전화번호' => $md['phone'] ?? '',
            ],
            '공사시공자 정보' => [
                '회사명' => $ct['company'] ?? '',
                '주소' => $ct['address'] ?? '',
                '성명' => $ct['name'] ?? '',
                '전화번호' => $ct['phone'] ?? '',
            ],
            '공사감리자 정보' => [
                '사무소명' => $sv['office'] ?? '',
                '주소' => $sv['address'] ?? '',
                '성명' => $sv['name'] ?? '',
                '전화번호' => $sv['phone'] ?? '',
            ],
        ];
    }

    /**
     * 요청서 문서번호 생성
     */
    private function generateRequestDocumentNo(int $tenantId): string
    {
        $prefix = 'REQ';
        $date = now()->format('Ymd');

        $lastNumber = Document::where('tenant_id', $tenantId)
            ->where('document_no', 'like', "{$prefix}-{$date}-%")
            ->orderByDesc('document_no')
            ->value('document_no');

        $sequence = $lastNumber ? (int) substr($lastNumber, -4) + 1 : 1;

        return sprintf('%s-%s-%04d', $prefix, $date, $sequence);
    }

    private function formatInspectionPeriod(array $options): string
    {
        $inspection = $options['inspection'] ?? [];
        $start = $inspection['start_date'] ?? '';
        $end = $inspection['end_date'] ?? '';

        if ($start && $end) {
            return "{$start}~{$end}";
        }

        return $start ?: $end ?: '';
    }
}
