<?php

namespace App\Services;

use App\Models\Orders\Order;
use App\Models\Production\WorkOrder;
use App\Models\Production\WorkOrderAssignee;
use App\Models\Production\WorkOrderBendingDetail;
use App\Models\Production\WorkOrderItem;
use App\Models\Tenants\Shipment;
use App\Models\Tenants\ShipmentItem;
use App\Services\Audit\AuditLogger;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class WorkOrderService extends Service
{
    private const AUDIT_TARGET = 'work_order';

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

    /**
     * 목록 조회 (검색/필터링/페이징)
     */
    public function index(array $params)
    {
        $tenantId = $this->tenantId();

        $page = (int) ($params['page'] ?? 1);
        $size = (int) ($params['size'] ?? 20);
        $q = trim((string) ($params['q'] ?? ''));
        $status = $params['status'] ?? null;
        $processId = $params['process_id'] ?? null;
        $processCode = $params['process_code'] ?? null;
        $assigneeId = $params['assignee_id'] ?? null;
        $assignedToMe = isset($params['assigned_to_me']) && $params['assigned_to_me'];
        $teamId = $params['team_id'] ?? null;
        $scheduledFrom = $params['scheduled_from'] ?? null;
        $scheduledTo = $params['scheduled_to'] ?? null;

        $query = WorkOrder::query()
            ->where('tenant_id', $tenantId)
            ->with([
                'assignee:id,name',
                'assignees.user:id,name',
                'team:id,name',
                'salesOrder:id,order_no,client_id,client_name',
                'salesOrder.client:id,name',
                'process:id,process_name,process_code',
            ]);

        // 검색어
        if ($q !== '') {
            $query->where(function ($qq) use ($q) {
                $qq->where('work_order_no', 'like', "%{$q}%")
                    ->orWhere('project_name', 'like', "%{$q}%");
            });
        }

        // 상태 필터
        if ($status !== null) {
            $query->where('status', $status);
        }

        // 공정 필터 (process_id)
        if ($processId !== null) {
            $query->where('process_id', $processId);
        }

        // 공정 코드 필터 (process_code) - 대시보드용
        if ($processCode !== null) {
            $query->whereHas('process', fn ($q) => $q->where('process_code', $processCode));
        }

        // 담당자 필터
        if ($assigneeId !== null) {
            $query->where('assignee_id', $assigneeId);
        }

        // 나에게 배정된 작업만 필터 (주 담당자 또는 공동 담당자)
        if ($assignedToMe) {
            $userId = $this->apiUserId();
            $query->where(function ($q) use ($userId) {
                $q->where('assignee_id', $userId)
                    ->orWhereHas('assignees', fn ($aq) => $aq->where('user_id', $userId));
            });
        }

        // 팀 필터
        if ($teamId !== null) {
            $query->where('team_id', $teamId);
        }

        // 예정일 범위
        if ($scheduledFrom !== null) {
            $query->where('scheduled_date', '>=', $scheduledFrom);
        }
        if ($scheduledTo !== null) {
            $query->where('scheduled_date', '<=', $scheduledTo);
        }

        $query->orderByDesc('created_at');

        return $query->paginate($size, ['*'], 'page', $page);
    }

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

        $counts = WorkOrder::where('tenant_id', $tenantId)
            ->select('status', DB::raw('count(*) as count'))
            ->groupBy('status')
            ->pluck('count', 'status')
            ->toArray();

        return [
            'total' => array_sum($counts),
            'unassigned' => $counts[WorkOrder::STATUS_UNASSIGNED] ?? 0,
            'pending' => $counts[WorkOrder::STATUS_PENDING] ?? 0,
            'waiting' => $counts[WorkOrder::STATUS_WAITING] ?? 0,
            'in_progress' => $counts[WorkOrder::STATUS_IN_PROGRESS] ?? 0,
            'completed' => $counts[WorkOrder::STATUS_COMPLETED] ?? 0,
            'shipped' => $counts[WorkOrder::STATUS_SHIPPED] ?? 0,
        ];
    }

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

        $workOrder = WorkOrder::where('tenant_id', $tenantId)
            ->with([
                'assignee:id,name',
                'assignees.user:id,name',
                'team:id,name',
                'salesOrder:id,order_no,site_name,client_id',
                'salesOrder.client:id,name',
                'process:id,process_name,process_code,work_steps',
                'items',
                'bendingDetail',
                'issues' => fn ($q) => $q->orderByDesc('created_at'),
            ])
            ->find($id);

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

        return $workOrder;
    }

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

        return DB::transaction(function () use ($data, $tenantId, $userId) {
            // 작업지시번호 자동 생성
            $data['work_order_no'] = $this->generateWorkOrderNo($tenantId);
            $data['tenant_id'] = $tenantId;
            $data['created_by'] = $userId;
            $data['updated_by'] = $userId;

            // 담당자가 있으면 상태를 pending으로
            if (! empty($data['assignee_id'])) {
                $data['status'] = $data['status'] ?? WorkOrder::STATUS_PENDING;
            }

            $items = $data['items'] ?? [];
            $bendingDetail = $data['bending_detail'] ?? null;
            $salesOrderId = $data['sales_order_id'] ?? null;
            unset($data['items'], $data['bending_detail']);

            $workOrder = WorkOrder::create($data);

            // process 관계 로드 (isBending 체크용)
            $workOrder->load('process:id,process_name,process_code');

            // 품목 저장: 직접 전달된 품목이 없고 수주 ID가 있으면 수주에서 복사
            if (empty($items) && $salesOrderId) {
                $salesOrder = \App\Models\Orders\Order::with('items')->find($salesOrderId);
                if ($salesOrder && $salesOrder->items->isNotEmpty()) {
                    foreach ($salesOrder->items as $index => $orderItem) {
                        $workOrder->items()->create([
                            'tenant_id' => $tenantId,
                            'source_order_item_id' => $orderItem->id, // 원본 수주 품목 추적용
                            'item_id' => $orderItem->item_id,
                            'item_name' => $orderItem->item_name,
                            'specification' => $orderItem->specification,
                            'quantity' => $orderItem->quantity,
                            'unit' => $orderItem->unit,
                            'sort_order' => $index,
                        ]);
                    }
                }
            } else {
                // 직접 전달된 품목 저장
                foreach ($items as $index => $item) {
                    $item['tenant_id'] = $tenantId;
                    $item['sort_order'] = $index;
                    $workOrder->items()->create($item);
                }
            }

            // 벤딩 상세 저장 (벤딩 공정인 경우)
            if ($workOrder->isBending() && $bendingDetail) {
                $bendingDetail['tenant_id'] = $tenantId;
                $workOrder->bendingDetail()->create($bendingDetail);
            }

            // 감사 로그
            $this->auditLogger->log(
                $tenantId,
                self::AUDIT_TARGET,
                $workOrder->id,
                'created',
                null,
                $workOrder->toArray()
            );

            return $workOrder->load(['assignee:id,name', 'assignees.user:id,name', 'team:id,name', 'process:id,process_name,process_code', 'items', 'bendingDetail']);
        });
    }

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

        $workOrder = WorkOrder::where('tenant_id', $tenantId)
            ->with('process:id,process_name,process_code')
            ->find($id);
        if (! $workOrder) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        $beforeData = $workOrder->toArray();

        return DB::transaction(function () use ($workOrder, $data, $userId, $beforeData) {
            $data['updated_by'] = $userId;

            $items = $data['items'] ?? null;
            $bendingDetail = $data['bending_detail'] ?? null;
            $assigneeIds = $data['assignee_ids'] ?? null;
            unset($data['items'], $data['bending_detail'], $data['assignee_ids'], $data['work_order_no']); // 번호 변경 불가

            // 품목 수정 시 기존 품목 기록
            $oldItems = null;
            if ($items !== null) {
                $oldItems = $workOrder->items()->get()->toArray();
            }

            // 담당자 수정 시 기존 담당자 기록
            $oldAssignees = null;
            if ($assigneeIds !== null) {
                $oldAssignees = $workOrder->assignees()->pluck('user_id')->toArray();
            }

            $workOrder->update($data);

            // 담당자 처리 (assignee_ids 배열)
            if ($assigneeIds !== null) {
                $assigneeIds = array_unique(array_filter($assigneeIds));

                // 기존 담당자 삭제 후 새로 추가
                $workOrder->assignees()->delete();

                foreach ($assigneeIds as $index => $assigneeId) {
                    WorkOrderAssignee::create([
                        'tenant_id' => $workOrder->tenant_id,
                        'work_order_id' => $workOrder->id,
                        'user_id' => $assigneeId,
                        'is_primary' => $index === 0, // 첫 번째가 주 담당자
                    ]);
                }

                // 주 담당자는 work_orders 테이블에도 설정 (하위 호환)
                $primaryAssigneeId = $assigneeIds[0] ?? null;
                $workOrder->assignee_id = $primaryAssigneeId;
                $workOrder->save();

                // 담당자 수정 감사 로그
                $this->auditLogger->log(
                    $workOrder->tenant_id,
                    self::AUDIT_TARGET,
                    $workOrder->id,
                    'assignees_updated',
                    ['assignee_ids' => $oldAssignees],
                    ['assignee_ids' => $assigneeIds]
                );
            }

            // 품목 부분 수정 (ID 기반 upsert/delete)
            if ($items !== null) {
                $existingIds = $workOrder->items()->pluck('id')->toArray();
                $incomingIds = [];

                foreach ($items as $index => $item) {
                    $itemData = [
                        'tenant_id' => $workOrder->tenant_id,
                        'item_name' => $item['item_name'] ?? null,
                        'specification' => $item['specification'] ?? null,
                        'quantity' => $item['quantity'] ?? 1,
                        'unit' => $item['unit'] ?? null,
                        'sort_order' => $index,
                    ];

                    if (isset($item['id']) && $item['id']) {
                        // ID가 있으면 업데이트
                        $existingItem = $workOrder->items()->find($item['id']);
                        if ($existingItem) {
                            $existingItem->update($itemData);
                            $incomingIds[] = (int) $item['id'];
                        }
                    } else {
                        // ID가 없으면 신규 생성
                        $newItem = $workOrder->items()->create($itemData);
                        $incomingIds[] = $newItem->id;
                    }
                }

                // 요청에 없는 기존 품목 삭제
                $toDelete = array_diff($existingIds, $incomingIds);
                if (! empty($toDelete)) {
                    $workOrder->items()->whereIn('id', $toDelete)->delete();
                }

                // 품목 수정 감사 로그
                $this->auditLogger->log(
                    $workOrder->tenant_id,
                    self::AUDIT_TARGET,
                    $workOrder->id,
                    'items_updated',
                    ['items' => $oldItems],
                    ['items' => $workOrder->items()->get()->toArray()]
                );
            }

            // 벤딩 상세 업데이트 (벤딩 공정인 경우에만)
            if ($bendingDetail !== null && $workOrder->isBending()) {
                $bendingDetail['tenant_id'] = $workOrder->tenant_id;
                $workOrder->bendingDetail()->updateOrCreate(
                    ['work_order_id' => $workOrder->id],
                    $bendingDetail
                );
            }

            // 수정 감사 로그
            $this->auditLogger->log(
                $workOrder->tenant_id,
                self::AUDIT_TARGET,
                $workOrder->id,
                'updated',
                $beforeData,
                $workOrder->fresh()->toArray()
            );

            return $workOrder->load(['assignee:id,name', 'assignees.user:id,name', 'team:id,name', 'process:id,process_name,process_code', 'items', 'bendingDetail']);
        });
    }

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

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

        // 진행 중이거나 완료된 작업은 삭제 불가
        if (in_array($workOrder->status, [
            WorkOrder::STATUS_IN_PROGRESS,
            WorkOrder::STATUS_COMPLETED,
            WorkOrder::STATUS_SHIPPED,
        ])) {
            throw new BadRequestHttpException(__('error.work_order.cannot_delete_in_progress'));
        }

        $beforeData = $workOrder->toArray();
        $workOrder->delete();

        // 삭제 감사 로그
        $this->auditLogger->log(
            $tenantId,
            self::AUDIT_TARGET,
            $workOrder->id,
            'deleted',
            $beforeData,
            null
        );

        return 'success';
    }

    /**
     * 상태 변경
     *
     * @param  int  $id  작업지시 ID
     * @param  string  $status  변경할 상태
     * @param  array|null  $resultData  완료 시 결과 데이터 (선택)
     */
    public function updateStatus(int $id, string $status, ?array $resultData = null)
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

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

        // 상태 유효성 검증
        if (! in_array($status, WorkOrder::STATUSES)) {
            throw new BadRequestHttpException(__('error.invalid_status'));
        }

        // Fast-track 완료 체크: pending/waiting에서 completed로 직접 전환 허용
        // 작업자 화면의 "전량완료" 버튼 지원
        $isFastTrackCompletion = $status === WorkOrder::STATUS_COMPLETED &&
            in_array($workOrder->status, [WorkOrder::STATUS_PENDING, WorkOrder::STATUS_WAITING]);

        // 일반 상태 전이 규칙 검증 (fast-track이 아닌 경우)
        if (! $isFastTrackCompletion && ! $workOrder->canTransitionTo($status)) {
            $allowed = implode(', ', $workOrder->getAllowedTransitions());
            throw new BadRequestHttpException(
                __('error.work_order.invalid_transition', [
                    'from' => $workOrder->status,
                    'to' => $status,
                    'allowed' => $allowed,
                ])
            );
        }

        return DB::transaction(function () use ($workOrder, $status, $resultData, $tenantId, $userId) {
            $oldStatus = $workOrder->status;
            $workOrder->status = $status;
            $workOrder->updated_by = $userId;

            // 상태에 따른 타임스탬프 업데이트
            switch ($status) {
                case WorkOrder::STATUS_IN_PROGRESS:
                    $workOrder->started_at = $workOrder->started_at ?? now();
                    break;
                case WorkOrder::STATUS_COMPLETED:
                    // Fast-track 완료의 경우 started_at도 설정 (중간 상태 생략)
                    $workOrder->started_at = $workOrder->started_at ?? now();
                    $workOrder->completed_at = now();
                    // 모든 품목에 결과 데이터 저장
                    $this->saveItemResults($workOrder, $resultData, $userId);
                    break;
                case WorkOrder::STATUS_SHIPPED:
                    $workOrder->shipped_at = now();
                    break;
            }

            $workOrder->save();

            // 상태 변경 감사 로그
            $this->auditLogger->log(
                $tenantId,
                self::AUDIT_TARGET,
                $workOrder->id,
                'status_changed',
                ['status' => $oldStatus],
                ['status' => $status]
            );

            // 연결된 수주(Order) 상태 동기화
            $this->syncOrderStatus($workOrder, $tenantId);

            // 작업완료 시 자동 출하 생성
            if ($status === WorkOrder::STATUS_COMPLETED) {
                $this->createShipmentFromWorkOrder($workOrder, $tenantId, $userId);
            }

            return $workOrder->load(['assignee:id,name', 'assignees.user:id,name', 'team:id,name', 'process:id,process_name,process_code']);
        });
    }

    /**
     * 작업지시 완료 시 자동 출하 생성
     *
     * 작업지시가 완료(completed) 상태가 되면 출하(Shipment)를 자동 생성하여 출하관리로 넘깁니다.
     * 발주처/배송 정보는 출하에 복사하지 않고, 수주(Order)를 참조합니다.
     * (Shipment 모델의 accessor 메서드로 수주 정보 참조)
     */
    private function createShipmentFromWorkOrder(WorkOrder $workOrder, int $tenantId, int $userId): ?Shipment
    {
        // 이미 이 작업지시에 연결된 출하가 있으면 스킵
        $existingShipment = Shipment::where('tenant_id', $tenantId)
            ->where('work_order_id', $workOrder->id)
            ->first();

        if ($existingShipment) {
            return $existingShipment;
        }

        // 출하번호 자동 생성
        $shipmentNo = Shipment::generateShipmentNo($tenantId);

        // 출하 생성 데이터
        // 발주처/배송 정보는 수주(Order)를 참조하므로 여기서 복사하지 않음
        $shipmentData = [
            'tenant_id' => $tenantId,
            'shipment_no' => $shipmentNo,
            'work_order_id' => $workOrder->id,
            'order_id' => $workOrder->sales_order_id,
            'scheduled_date' => now()->toDateString(), // 오늘 날짜로 출하 예정
            'status' => 'scheduled', // 예정 상태로 생성
            'priority' => 'normal',
            'delivery_method' => 'pickup', // 기본값
            'can_ship' => true, // 생산 완료 후 생성되므로 출하가능
            'created_by' => $userId,
            'updated_by' => $userId,
        ];

        $shipment = Shipment::create($shipmentData);

        // 작업지시 품목을 출하 품목으로 복사
        $this->copyWorkOrderItemsToShipment($workOrder, $shipment, $tenantId);

        // 자동 출하 생성 감사 로그
        $this->auditLogger->log(
            $tenantId,
            'shipment',
            $shipment->id,
            'auto_created_from_work_order',
            null,
            [
                'work_order_id' => $workOrder->id,
                'shipment_no' => $shipmentNo,
                'items_count' => $shipment->items()->count(),
            ]
        );

        return $shipment;
    }

    /**
     * 작업지시 품목을 출하 품목으로 복사
     *
     * 작업지시 품목(work_order_items)의 정보를 출하 품목(shipment_items)으로 복사합니다.
     * 작업지시 품목이 없으면 수주 품목(order_items)을 대체 사용합니다.
     * LOT 번호는 작업지시 품목의 결과 데이터에서 가져옵니다.
     * 층/부호(floor_unit)는 원본 수주품목(order_items)에서 가져옵니다.
     */
    private function copyWorkOrderItemsToShipment(WorkOrder $workOrder, Shipment $shipment, int $tenantId): void
    {
        $workOrderItems = $workOrder->items()->get();

        // 작업지시 품목이 있으면 사용
        if ($workOrderItems->isNotEmpty()) {
            foreach ($workOrderItems as $index => $woItem) {
                // 작업지시 품목의 결과 데이터에서 LOT 번호 추출
                $result = $woItem->options['result'] ?? [];
                $lotNo = $result['lot_no'] ?? null;

                // 원본 수주품목에서 층/부호 정보 조회
                $floorUnit = $this->getFloorUnitFromOrderItem($woItem->source_order_item_id, $tenantId);

                // 출하 품목 생성
                ShipmentItem::create([
                    'tenant_id' => $tenantId,
                    'shipment_id' => $shipment->id,
                    'seq' => $index + 1,
                    'item_code' => $woItem->item_id ? "ITEM-{$woItem->item_id}" : null,
                    'item_name' => $woItem->item_name,
                    'floor_unit' => $floorUnit,
                    'specification' => $woItem->specification,
                    'quantity' => $result['good_qty'] ?? $woItem->quantity, // 양품 수량 우선
                    'unit' => $woItem->unit,
                    'lot_no' => $lotNo,
                    'remarks' => null,
                ]);
            }

            return;
        }

        // 작업지시 품목이 없으면 수주 품목에서 복사 (Fallback)
        if ($workOrder->salesOrder) {
            $orderItems = $workOrder->salesOrder->items()->get();

            foreach ($orderItems as $index => $orderItem) {
                // 수주품목에서 층/부호 정보 조회
                $floorUnit = $this->getFloorUnitFromOrderItem($orderItem->id, $tenantId);

                // 출하 품목 생성
                ShipmentItem::create([
                    'tenant_id' => $tenantId,
                    'shipment_id' => $shipment->id,
                    'seq' => $index + 1,
                    'item_code' => $orderItem->item_id ? "ITEM-{$orderItem->item_id}" : null,
                    'item_name' => $orderItem->item_name,
                    'floor_unit' => $floorUnit,
                    'specification' => $orderItem->specification,
                    'quantity' => $orderItem->quantity,
                    'unit' => $orderItem->unit,
                    'lot_no' => null, // 수주 품목에는 LOT 번호 없음
                    'remarks' => null,
                ]);
            }
        }
    }

    /**
     * 수주품목에서 층/부호 정보 조회
     *
     * floor_code와 symbol_code를 조합하여 floor_unit 형식으로 반환합니다.
     * 예: floor_code='3층', symbol_code='A호' → '3층/A호'
     */
    private function getFloorUnitFromOrderItem(?int $orderItemId, int $tenantId): ?string
    {
        if (! $orderItemId) {
            return null;
        }

        $orderItem = \App\Models\Orders\OrderItem::where('tenant_id', $tenantId)
            ->find($orderItemId);

        if (! $orderItem) {
            return null;
        }

        $parts = array_filter([
            $orderItem->floor_code,
            $orderItem->symbol_code,
        ]);

        return ! empty($parts) ? implode('/', $parts) : null;
    }

    /**
     * 작업지시 상태 변경 시 연결된 수주(Order) 상태 동기화
     *
     * 매핑 규칙:
     * - WorkOrder::STATUS_IN_PROGRESS → Order::STATUS_IN_PRODUCTION (생산중)
     * - WorkOrder::STATUS_COMPLETED → Order::STATUS_PRODUCED (생산완료)
     * - WorkOrder::STATUS_SHIPPED → Order::STATUS_SHIPPED (출하완료)
     */
    private function syncOrderStatus(WorkOrder $workOrder, int $tenantId): void
    {
        // 수주 연결이 없으면 스킵
        if (! $workOrder->sales_order_id) {
            return;
        }

        $order = Order::where('tenant_id', $tenantId)->find($workOrder->sales_order_id);
        if (! $order) {
            return;
        }

        // 작업지시 상태 → 수주 상태 매핑
        $statusMap = [
            WorkOrder::STATUS_IN_PROGRESS => Order::STATUS_IN_PRODUCTION,
            WorkOrder::STATUS_COMPLETED => Order::STATUS_PRODUCED,
            WorkOrder::STATUS_SHIPPED => Order::STATUS_SHIPPED,
        ];

        $newOrderStatus = $statusMap[$workOrder->status] ?? null;

        // 매핑되는 상태가 없거나 이미 동일한 상태면 스킵
        if (! $newOrderStatus || $order->status_code === $newOrderStatus) {
            return;
        }

        $oldOrderStatus = $order->status_code;
        $order->status_code = $newOrderStatus;
        $order->updated_by = $this->apiUserId();
        $order->save();

        // 수주 상태 동기화 감사 로그
        $this->auditLogger->log(
            $tenantId,
            'order',
            $order->id,
            'status_synced_from_work_order',
            ['status_code' => $oldOrderStatus, 'work_order_id' => $workOrder->id],
            ['status_code' => $newOrderStatus, 'work_order_id' => $workOrder->id]
        );
    }

    /**
     * 작업지시 품목에 결과 데이터 저장
     */
    private function saveItemResults(WorkOrder $workOrder, ?array $resultData, int $userId): void
    {
        $items = $workOrder->items;
        $lotNo = $this->generateLotNo($workOrder);

        foreach ($items as $item) {
            $itemResult = [
                'completed_at' => now()->toDateTimeString(),
                'good_qty' => $item->quantity, // 기본값: 지시수량 전체가 양품
                'defect_qty' => 0,
                'defect_rate' => 0,
                'lot_no' => $lotNo,
                'is_inspected' => false,
                'is_packaged' => false,
                'worker_id' => $userId,
                'memo' => null,
            ];

            // 개별 품목 결과 데이터가 있으면 병합
            if ($resultData && isset($resultData['items'][$item->id])) {
                $itemResult = array_merge($itemResult, $resultData['items'][$item->id]);
                // 불량률 재계산
                $totalQty = ($itemResult['good_qty'] ?? 0) + ($itemResult['defect_qty'] ?? 0);
                $itemResult['defect_rate'] = $totalQty > 0
                    ? round(($itemResult['defect_qty'] / $totalQty) * 100, 2)
                    : 0;
            }

            // 품목 상태도 완료로 변경
            $item->status = WorkOrderItem::STATUS_COMPLETED;
            $options = $item->options ?? [];
            $options['result'] = $itemResult;
            $item->options = $options;
            $item->save();
        }
    }

    /**
     * LOT 번호 생성
     */
    private function generateLotNo(WorkOrder $workOrder): string
    {
        $date = now()->format('ymd');
        $prefix = 'KD-SA';

        // 오늘 날짜의 마지막 LOT 번호 조회
        $lastLotNo = WorkOrderItem::where('tenant_id', $workOrder->tenant_id)
            ->whereNotNull('options->result->lot_no')
            ->where('options->result->lot_no', 'like', "{$prefix}-{$date}-%")
            ->orderByDesc('id')
            ->value('options->result->lot_no');

        if ($lastLotNo) {
            // 마지막 번호에서 시퀀스 추출 후 증가
            $parts = explode('-', $lastLotNo);
            $seq = (int) end($parts) + 1;
        } else {
            $seq = 1;
        }

        return sprintf('%s-%s-%02d', $prefix, $date, $seq);
    }

    /**
     * 담당자 배정 (다중 담당자 지원)
     *
     * @param  int  $id  작업지시 ID
     * @param  array  $data  배정 데이터 (assignee_ids: int[], team_id?: int)
     */
    public function assign(int $id, array $data)
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

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

        return DB::transaction(function () use ($workOrder, $data, $tenantId, $userId) {
            // 이전 상태 기록
            $beforeAssignees = $workOrder->assignees()->pluck('user_id')->toArray();
            $beforePrimaryAssignee = $workOrder->assignee_id;
            $beforeTeam = $workOrder->team_id;

            // 담당자 ID 배열 처리 (단일 값도 배열로 변환)
            $assigneeIds = $data['assignee_ids'] ?? [];
            if (isset($data['assignee_id']) && ! empty($data['assignee_id'])) {
                // 하위 호환: 단일 assignee_id도 지원
                $assigneeIds = is_array($data['assignee_id']) ? $data['assignee_id'] : [$data['assignee_id']];
            }
            $assigneeIds = array_unique(array_filter($assigneeIds));

            // 기존 담당자 삭제 후 새로 추가
            $workOrder->assignees()->delete();

            foreach ($assigneeIds as $index => $assigneeId) {
                WorkOrderAssignee::create([
                    'tenant_id' => $tenantId,
                    'work_order_id' => $workOrder->id,
                    'user_id' => $assigneeId,
                    'is_primary' => $index === 0, // 첫 번째가 주 담당자
                ]);
            }

            // 주 담당자는 work_orders 테이블에도 설정 (하위 호환)
            $primaryAssigneeId = $assigneeIds[0] ?? null;
            $workOrder->assignee_id = $primaryAssigneeId;
            $workOrder->team_id = $data['team_id'] ?? $workOrder->team_id;
            $workOrder->updated_by = $userId;

            // 미배정이었으면 대기로 변경
            if ($workOrder->status === WorkOrder::STATUS_UNASSIGNED && $primaryAssigneeId) {
                $workOrder->status = WorkOrder::STATUS_PENDING;
            }

            $workOrder->save();

            // 배정 감사 로그
            $this->auditLogger->log(
                $tenantId,
                self::AUDIT_TARGET,
                $workOrder->id,
                'assigned',
                [
                    'assignee_id' => $beforePrimaryAssignee,
                    'assignee_ids' => $beforeAssignees,
                    'team_id' => $beforeTeam,
                ],
                [
                    'assignee_id' => $workOrder->assignee_id,
                    'assignee_ids' => $assigneeIds,
                    'team_id' => $workOrder->team_id,
                ]
            );

            return $workOrder->load(['assignee:id,name', 'assignees.user:id,name', 'team:id,name', 'process:id,process_name,process_code']);
        });
    }

    /**
     * 벤딩 항목 토글
     */
    public function toggleBendingField(int $id, string $field)
    {
        $tenantId = $this->tenantId();

        $workOrder = WorkOrder::where('tenant_id', $tenantId)
            ->with('process:id,process_name,process_code')
            ->find($id);
        if (! $workOrder) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        if (! $workOrder->isBending()) {
            throw new BadRequestHttpException(__('error.work_order.not_bending_process'));
        }

        $detail = $workOrder->bendingDetail;
        if (! $detail) {
            $detail = $workOrder->bendingDetail()->create([
                'tenant_id' => $workOrder->tenant_id,
            ]);
        }

        if (! in_array($field, WorkOrderBendingDetail::PROCESS_FIELDS)) {
            throw new BadRequestHttpException(__('error.invalid_field'));
        }

        $beforeValue = $detail->{$field};
        $detail->toggleField($field);

        // 벤딩 토글 감사 로그
        $this->auditLogger->log(
            $workOrder->tenant_id,
            self::AUDIT_TARGET,
            $workOrder->id,
            'bending_toggled',
            [$field => $beforeValue],
            [$field => $detail->{$field}]
        );

        return $detail;
    }

    /**
     * 이슈 추가
     */
    public function addIssue(int $workOrderId, array $data)
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

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

        $data['tenant_id'] = $tenantId;
        $data['reported_by'] = $userId;

        $issue = $workOrder->issues()->create($data);

        // 이슈 추가 감사 로그
        $this->auditLogger->log(
            $tenantId,
            self::AUDIT_TARGET,
            $workOrderId,
            'issue_added',
            null,
            ['issue_id' => $issue->id, 'title' => $issue->title, 'priority' => $issue->priority]
        );

        return $issue;
    }

    /**
     * 이슈 해결
     */
    public function resolveIssue(int $workOrderId, int $issueId)
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

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

        $issue = $workOrder->issues()->find($issueId);
        if (! $issue) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        $issue->resolve($userId);

        // 이슈 해결 감사 로그
        $this->auditLogger->log(
            $tenantId,
            self::AUDIT_TARGET,
            $workOrderId,
            'issue_resolved',
            ['issue_id' => $issueId, 'status' => 'open'],
            ['issue_id' => $issueId, 'status' => 'resolved', 'resolved_by' => $userId]
        );

        return $issue;
    }

    /**
     * 작업지시번호 자동 생성
     */
    private function generateWorkOrderNo(int $tenantId): string
    {
        $prefix = 'WO';
        $date = now()->format('Ymd');

        // 오늘 날짜 기준 마지막 번호 조회
        $lastNo = WorkOrder::withoutGlobalScopes()
            ->where('tenant_id', $tenantId)
            ->where('work_order_no', 'like', "{$prefix}{$date}%")
            ->orderByDesc('work_order_no')
            ->value('work_order_no');

        if ($lastNo) {
            $seq = (int) substr($lastNo, -4) + 1;
        } else {
            $seq = 1;
        }

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

    /**
     * 품목 상태 변경
     */
    public function updateItemStatus(int $workOrderId, int $itemId, string $status)
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

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

        $item = $workOrder->items()->find($itemId);
        if (! $item) {
            throw new NotFoundHttpException(__('error.not_found'));
        }

        // 상태 유효성 검증
        if (! in_array($status, WorkOrderItem::STATUSES)) {
            throw new BadRequestHttpException(__('error.invalid_status'));
        }

        $beforeStatus = $item->status;
        $item->status = $status;
        $item->save();

        // 감사 로그
        $this->auditLogger->log(
            $tenantId,
            self::AUDIT_TARGET,
            $workOrderId,
            'item_status_changed',
            ['item_id' => $itemId, 'status' => $beforeStatus],
            ['item_id' => $itemId, 'status' => $status]
        );

        // 작업지시 상태 자동 연동
        $workOrderStatusChanged = $this->syncWorkOrderStatusFromItems($workOrder);

        // 품목과 함께 작업지시 상태도 반환
        return [
            'item' => $item,
            'work_order_status' => $workOrder->fresh()->status,
            'work_order_status_changed' => $workOrderStatusChanged,
        ];
    }

    /**
     * 작업지시에 필요한 자재 목록 조회 (BOM 기반)
     *
     * 작업지시의 품목에 연결된 BOM 자재 목록을 반환합니다.
     * 현재는 품목 정보 기반으로 Mock 데이터를 반환하며,
     * 향후 자재 관리 기능 구현 시 실제 데이터로 연동됩니다.
     *
     * @param  int  $workOrderId  작업지시 ID
     * @return array 자재 목록 (id, material_code, material_name, unit, current_stock, fifo_rank)
     */
    public function getMaterials(int $workOrderId): array
    {
        $tenantId = $this->tenantId();

        $workOrder = WorkOrder::where('tenant_id', $tenantId)
            ->with(['items.item', 'salesOrder.items'])
            ->find($workOrderId);

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

        $materials = [];
        $rank = 1;

        // 1. WorkOrder 자체 items가 있으면 사용
        if ($workOrder->items && $workOrder->items->count() > 0) {
            foreach ($workOrder->items as $item) {
                $materials[] = [
                    'id' => $item->id,
                    'material_code' => $item->item_id ? "MAT-{$item->item_id}" : "MAT-{$item->id}",
                    'material_name' => $item->item_name ?? '자재 '.$item->id,
                    'unit' => $item->unit ?? 'EA',
                    'current_stock' => 100, // Mock: 실제 재고 데이터 연동 필요
                    'fifo_rank' => $rank++,
                ];
            }
        }
        // 2. WorkOrder items가 없으면 SalesOrder items 사용
        elseif ($workOrder->salesOrder && $workOrder->salesOrder->items && $workOrder->salesOrder->items->count() > 0) {
            foreach ($workOrder->salesOrder->items as $item) {
                $materials[] = [
                    'id' => $item->id,
                    'material_code' => $item->item_id ? "MAT-{$item->item_id}" : "SO-{$item->id}",
                    'material_name' => $item->item_name ?? '자재 '.$item->id,
                    'unit' => $item->unit ?? 'EA',
                    'current_stock' => 100, // Mock: 실제 재고 데이터 연동 필요
                    'fifo_rank' => $rank++,
                ];
            }
        }

        return $materials;
    }

    /**
     * 자재 투입 등록
     *
     * 작업지시에 자재 투입을 등록합니다.
     * 현재는 감사 로그만 기록하며, 향후 재고 차감 로직 추가 필요.
     *
     * @param  int  $workOrderId  작업지시 ID
     * @param  array  $materialIds  투입할 자재 ID 목록
     * @return array 투입 결과
     */
    public function registerMaterialInput(int $workOrderId, array $materialIds): array
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

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

        // 자재 투입 감사 로그
        $this->auditLogger->log(
            $tenantId,
            self::AUDIT_TARGET,
            $workOrderId,
            'material_input',
            null,
            [
                'material_ids' => $materialIds,
                'input_by' => $userId,
                'input_at' => now()->toDateTimeString(),
            ]
        );

        return [
            'work_order_id' => $workOrderId,
            'material_count' => count($materialIds),
            'input_at' => now()->toDateTimeString(),
        ];
    }

    /**
     * 품목 상태 기반으로 작업지시 상태 자동 동기화
     *
     * 규칙:
     * - 품목 중 하나라도 in_progress → 작업지시 in_progress (pending에서도 자동 전환)
     * - 모든 품목이 completed → 작업지시 completed
     * - 모든 품목이 waiting → 작업지시 waiting (단, waiting 이상인 경우만)
     * - 미배정(unassigned) 상태에서는 동기화하지 않음
     *
     * @return bool 상태 변경 여부
     */
    private function syncWorkOrderStatusFromItems(WorkOrder $workOrder): bool
    {
        // 품목이 없으면 동기화하지 않음
        $items = $workOrder->items()->get();
        if ($items->isEmpty()) {
            return false;
        }

        // 미배정(unassigned) 상태에서는 동기화하지 않음 (배정 없이 작업 시작 불가)
        if ($workOrder->status === WorkOrder::STATUS_UNASSIGNED) {
            return false;
        }

        // 품목 상태 집계
        $statusCounts = $items->groupBy('status')->map->count();
        $totalItems = $items->count();

        $waitingCount = $statusCounts->get(WorkOrderItem::STATUS_WAITING, 0);
        $inProgressCount = $statusCounts->get(WorkOrderItem::STATUS_IN_PROGRESS, 0);
        $completedCount = $statusCounts->get(WorkOrderItem::STATUS_COMPLETED, 0);

        // 새 상태 결정
        $newStatus = null;
        if ($inProgressCount > 0) {
            // 하나라도 진행중이면 작업지시도 진행중
            $newStatus = WorkOrder::STATUS_IN_PROGRESS;
        } elseif ($completedCount === $totalItems) {
            // 모두 완료면 작업지시도 완료
            $newStatus = WorkOrder::STATUS_COMPLETED;
        } elseif ($waitingCount === $totalItems) {
            // 모두 대기면 작업지시도 대기
            $newStatus = WorkOrder::STATUS_WAITING;
        }

        // 상태가 변경되어야 하고, 현재와 다른 경우에만 업데이트
        if ($newStatus && $newStatus !== $workOrder->status) {
            $oldStatus = $workOrder->status;
            $workOrder->status = $newStatus;

            // 상태에 따른 타임스탬프 업데이트
            if ($newStatus === WorkOrder::STATUS_IN_PROGRESS && ! $workOrder->started_at) {
                $workOrder->started_at = now();
            } elseif ($newStatus === WorkOrder::STATUS_COMPLETED) {
                $workOrder->completed_at = now();
            }

            $workOrder->save();

            // 상태 변경 감사 로그
            $this->auditLogger->log(
                $workOrder->tenant_id,
                self::AUDIT_TARGET,
                $workOrder->id,
                'status_synced_from_items',
                ['status' => $oldStatus],
                ['status' => $newStatus]
            );

            // 완료 시 수주 상태 동기화 및 자동 출하 생성
            if ($newStatus === WorkOrder::STATUS_COMPLETED) {
                $this->syncOrderStatus($workOrder, $workOrder->tenant_id);
                $this->createShipmentFromWorkOrder($workOrder, $workOrder->tenant_id, $this->apiUserId());
            }

            return true;
        }

        return false;
    }
}
