<?php

namespace App\Services;

use App\Models\Order;
use App\Models\OrderDetail;
use App\Models\OrderProgressLog;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class ProgressService
{
    private array $coreFlow = ['baru', 'proses', 'selesai', 'diambil'];

    public function updateCoreStatus(Order $order, string $nextStatus, ?User $user = null, ?string $note = null): Order
    {
        return DB::transaction(function () use ($order, $nextStatus, $user, $note): Order {
            $order = Order::query()->lockForUpdate()->findOrFail($order->id);
            $this->guardCoreTransition($order, $nextStatus);

            $order->update([
                'status' => $nextStatus,
                'picked_up_at' => $nextStatus === 'diambil' ? now() : $order->picked_up_at,
            ]);

            $this->log($order, $this->progressForCore($nextStatus), $user, $note, $nextStatus);

            return $order->refresh();
        });
    }

    public function updateProgress(Order $order, string $progressStatus, ?User $user = null, ?string $note = null): OrderProgressLog
    {
        if (! in_array($progressStatus, Order::PROGRESS_STATUSES, true)) {
            throw ValidationException::withMessages(['progress_status' => 'Progress tidak valid.']);
        }

        return $this->log($order, $progressStatus, $user, $note, $order->status);
    }

    public function updateDetailProgress(Order $order, OrderDetail $detail, string $progressStatus, ?User $user = null, ?string $note = null): OrderDetail
    {
        if ((int) $detail->order_id !== (int) $order->id) {
            throw ValidationException::withMessages(['order_detail' => 'Item tidak sesuai dengan order.']);
        }

        if (! in_array($progressStatus, Order::PROGRESS_STATUSES, true)) {
            throw ValidationException::withMessages(['progress_status' => 'Progress item tidak valid.']);
        }

        return DB::transaction(function () use ($order, $detail, $progressStatus, $user, $note): OrderDetail {
            $detail = OrderDetail::query()->lockForUpdate()->findOrFail($detail->id);
            $detail->update([
                'progress_status' => $progressStatus,
                'progress_note' => $note,
                'progress_updated_at' => now(),
            ]);

            $this->log($order, $progressStatus, $user, trim("Item {$detail->product_name}. ".($note ?? '')), $order->status);

            return $detail->refresh();
        });
    }

    private function guardCoreTransition(Order $order, string $nextStatus): void
    {
        if (! in_array($nextStatus, $this->coreFlow, true)) {
            throw ValidationException::withMessages(['status' => 'Status laundry tidak valid.']);
        }

        if ($nextStatus === 'diambil' && ! $order->isPaid()) {
            throw ValidationException::withMessages(['payment_status' => 'Order hanya bisa diambil jika pembayaran lunas.']);
        }

        $current = array_search($order->status, $this->coreFlow, true);
        $next = array_search($nextStatus, $this->coreFlow, true);

        if ($next < $current || $next > $current + 1) {
            throw ValidationException::withMessages(['status' => 'Status laundry tidak boleh lompat sembarangan.']);
        }
    }

    private function progressForCore(string $core): string
    {
        return match ($core) {
            'baru' => 'order_diterima',
            'proses' => 'sedang_dicuci',
            'selesai' => 'siap_diambil_atau_dikirim',
            'diambil' => 'sudah_diambil_atau_diterima',
        };
    }

    private function log(Order $order, string $progressStatus, ?User $user, ?string $note, ?string $coreStatus): OrderProgressLog
    {
        $log = OrderProgressLog::query()->create([
            'order_id' => $order->id,
            'user_id' => $user?->id,
            'core_status' => $coreStatus,
            'progress_status' => $progressStatus,
            'note' => $note,
        ]);

        app(CustomerNotificationService::class)->notifyOrderProgress($order, $progressStatus, $note);

        return $log;
    }
}
