<?php

namespace App\Services;

use App\Models\Order;
use App\Models\StockCode;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use RuntimeException;

class FulfillmentService
{
    public function __construct(
        private StockCodeInventoryService $inventory,
        private NotificationService $notifications,
        private OrderRewardService $rewards,
    ) {}

    public function claim(Order $order, User $actor): Order
    {
        return DB::transaction(function () use ($order, $actor) {
            $order = Order::whereKey($order->id)->lockForUpdate()->firstOrFail();

            if ($order->assigned_to && $order->assigned_to !== $actor->id) {
                throw new RuntimeException('Order is already claimed by another staff member.');
            }

            $order->update([
                'assigned_to' => $actor->id,
                'claimed_at' => $order->claimed_at ?? now(),
                'fulfillment_status' => $order->fulfillment_status === 'pending' ? 'claimed' : $order->fulfillment_status,
            ]);

            $this->timeline($order, $actor, 'claimed', 'تم استلام الطلب للتنفيذ');

            return $order;
        });
    }

    public function markPaid(Order $order, User $actor): void
    {
        $order->update([
            'payment_status' => 'paid',
            'paid_at' => now(),
        ]);

        $this->rewards->apply($order);
        $this->timeline($order, $actor, 'paid', 'تم تعليم الطلب كمدفوع');
    }

    public function complete(Order $order, User $actor, ?string $note = null): void
    {
        DB::transaction(function () use ($order, $actor, $note) {
            $order = Order::whereKey($order->id)->with('items')->lockForUpdate()->firstOrFail();

            foreach ($order->items as $item) {
                if ($item->delivery_method_snapshot === 'stored_code') {
                    $codes = StockCode::where('reserved_order_id', $order->id)
                        ->where('sold_order_item_id', $item->id)
                        ->where('status', 'reserved')
                        ->lockForUpdate()
                        ->get();

                    if ($codes->isEmpty()) {
                        throw new RuntimeException('No reserved stored codes found for this order.');
                    }

                    foreach ($codes as $code) {
                        $code->update([
                            'status' => 'sold',
                            'sold_order_item_id' => $item->id,
                            'sold_at' => now(),
                        ]);
                    }

                    $item->update([
                        'fulfillment_payload' => [
                            'stock_code_ids' => $codes->pluck('id')->values()->all(),
                            'masked_codes' => $codes->map->maskedCode()->values()->all(),
                        ],
                    ]);
                }

                $item->update(['fulfillment_status' => 'completed']);
            }

            $order->update([
                'assigned_to' => $order->assigned_to ?: $actor->id,
                'claimed_at' => $order->claimed_at ?? now(),
                'fulfillment_status' => 'completed',
                'completed_at' => now(),
                'fulfillment_note' => $note,
            ]);

            $this->timeline($order, $actor, 'completed', 'تم تنفيذ الطلب', $note);
            $this->notifications->fulfillmentCompleted($order);
        });
    }

    public function fail(Order $order, User $actor, string $reason): void
    {
        DB::transaction(function () use ($order, $actor, $reason) {
            $order = Order::whereKey($order->id)->lockForUpdate()->firstOrFail();

            $order->update([
                'assigned_to' => $order->assigned_to ?: $actor->id,
                'claimed_at' => $order->claimed_at ?? now(),
                'fulfillment_status' => 'failed',
                'failed_at' => now(),
                'failure_reason' => $reason,
            ]);

            $this->inventory->releaseReservedForOrder($order->id);
            $this->timeline($order, $actor, 'failed', 'فشل تنفيذ الطلب', $reason);
            $this->notifications->fulfillmentFailed($order, $reason);
        });
    }

    public function requestInfo(Order $order, User $actor, string $note): void
    {
        $order->update([
            'assigned_to' => $order->assigned_to ?: $actor->id,
            'claimed_at' => $order->claimed_at ?? now(),
            'fulfillment_status' => 'info_requested',
            'info_requested_at' => now(),
            'fulfillment_note' => $note,
        ]);

        $this->timeline($order, $actor, 'info_requested', 'تم طلب معلومات من العميل', $note);
        $this->notifications->infoRequested($order, $note);
    }

    private function timeline(Order $order, User $actor, string $event, string $title, ?string $note = null): void
    {
        $order->timelines()->create([
            'actor_id' => $actor->id,
            'event' => $event,
            'title' => $title,
            'note' => $note,
        ]);
    }
}
