<?php

namespace App\Services;

use App\Models\OrderItemModel;
use App\Models\OrderModel;
use App\Models\ProductionModel;

/** Creates and keeps item-level production records in sync for PO orders. */
class ProductionQueueService
{
    private OrderModel $orderModel;
    private OrderItemModel $itemModel;
    private ProductionModel $productionModel;

    public function __construct(
        ?OrderModel $orderModel = null,
        ?OrderItemModel $itemModel = null,
        ?ProductionModel $productionModel = null
    ) {
        $this->orderModel = $orderModel ?? new OrderModel();
        $this->itemModel = $itemModel ?? new OrderItemModel();
        $this->productionModel = $productionModel ?? new ProductionModel();
    }

    public function createForOrder(int $orderId, ?string $targetDate = null): void
    {
        $order = $this->orderModel->find($orderId);
        if (!$order || ($order['order_type'] ?? null) !== 'po') {
            return;
        }

        $db = \Config\Database::connect();
        $items = $this->itemModel->where('id_order', $orderId)->findAll();
        $scheduler = new MonthlyPoScheduler();
        $schedules = $scheduler->getOrderSchedule($orderId);
        if ($schedules === []) {
            $schedules = [[
                'scheduled_month' => date('Y-m-01', strtotime($targetDate ?? ($order['target_date'] ?? 'today'))),
                'quantity' => array_sum(array_column($items, 'qty')),
            ]];
        }

        foreach ($schedules as $schedule) {
            $scheduledMonth = date('Y-m-01', strtotime($schedule['scheduled_month']));
            $existingHeader = $this->productionModel
                ->where('id_order', $orderId)
                ->where('scheduled_month', $scheduledMonth)
                ->first();

            if (! $existingHeader && ! $this->productionModel->insert([
                'id_order' => $orderId,
                'scheduled_month' => $scheduledMonth,
                'production_number' => $this->productionModel->generateProductionNumber(),
                'start_date' => null,
                'finish_date' => null,
                'status' => 'waiting',
                'progress' => 0,
            ])) {
                throw new \RuntimeException('Gagal membuat batch produksi.');
            }
        }

        $plannedItems = MonthlyPoScheduler::distributeItems(
            $items,
            $schedules,
            $targetDate ?? ($order['target_date'] ?? null)
        );

        foreach ($plannedItems as $plannedItem) {
            $item = $plannedItem['item'];
            $productId = (int) ($item['id_product'] ?? 0);
            $qty = (int) $plannedItem['quantity'];
            $scheduledDate = $plannedItem['scheduled_month'];

            if ($productId <= 0 || $qty <= 0 || $this->queueExists($orderId, $productId, $scheduledDate)) {
                continue;
            }

            if (!$db->table('production_queue')->insert([
                'id_order' => $orderId,
                'id_product' => $productId,
                'qty_order' => $qty,
                'qty_hari_ini' => 0,
                'qty_sisa' => $qty,
                'tanggal_produksi' => $scheduledDate,
                'periode' => (int) date('Ym', strtotime($scheduledDate ?: 'today')),
                'status' => 'antrian',
            ])) {
                throw new \RuntimeException('Gagal menambahkan item produksi.');
            }
        }

        if (!$this->orderModel->update($orderId, [
            'order_status' => 'production',
            'production_status' => $this->orderModel->normalizeProductionStatus('waiting'),
        ])) {
            throw new \RuntimeException('Gagal memperbarui status produksi order.');
        }
    }

    private function queueExists(int $orderId, int $productId, string $scheduledDate): bool
    {
        $row = \Config\Database::connect()
            ->table('production_queue')
            ->select('id_queue')
            ->where('id_order', $orderId)
            ->where('id_product', $productId)
            ->where('tanggal_produksi', $scheduledDate)
            ->limit(1)
            ->get()
            ->getRowArray();

        return !empty($row);
    }
}
