<?php

namespace App\Services;

use App\Models\Cart;
use App\Models\Client;
use Illuminate\Support\Arr;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;

class CartService
{
    public function getAll(): Collection
    {
        return Cart::query()
            ->with(['items.schedule'])
            ->orderBy('created_at', 'desc')
            ->get();
    }

    public function findById(int $id): ?Cart
    {
        return Cart::query()
            ->with(['items.schedule'])
            ->find($id);
    }

    public function create(array $data): Cart
    {
        return Cart::create($data);
    }

    public function update(int $id, array $data): ?Cart
    {
        $model = $this->findById($id);

        if (! $model) {
            return null;
        }

        $model->update($data);

        return $model->fresh();
    }

    public function delete(int $id): bool
    {
        $model = $this->findById($id);

        if (! $model) {
            return false;
        }

        return $model->delete();
    }

    //

    public function getByUserId(int $userId): Collection
    {
        return Cart::query()
            ->with(['items.schedule.client.user', 'items.schedule.provider.user', 'items.schedule.address'])
            ->whereHas('client', fn ($query) => $query->where('user_id', $userId))
            ->orderBy('created_at', 'desc')
            ->get();
    }

    public function createForUser(array $data, int $userId): Cart
    {
        $client = Client::query()
            ->where('user_id', $userId)
            ->firstOrFail();

        $data['client_id'] = $client->id;

        return $this->hasSchedulePayload($data)
            ? $this->createWithSchedules($data)
            : $this->create($data);
    }

    public function createWithSchedules(array $data): Cart
    {
        return DB::transaction(function () use ($data) {
            $items = $this->scheduleItems($data);

            $schedules = app(ScheduleService::class)->createSingleOrMultiple(
                baseData:  $this->baseScheduleData($data),
                schedules: $items,
            );

            $cart = Cart::create([
                'client_id' => $data['client_id'],
            ]);

            foreach ($schedules as $schedule) {
                $cart->items()->create([
                    'schedule_id' => $schedule->id,
                ]);
            }

            return $cart->fresh(['items.schedule.client.user', 'items.schedule.provider.user', 'items.schedule.address']);
        });
    }

    //

    private function baseScheduleData(array $data): array
    {
        return Arr::except($data, [
            'schedules',
            'dates',
            'date',
            'period_type',
            'start_time',
            'end_time',
            'total_amount',
            'offers_meal',
        ]);
    }

    private function hasSchedulePayload(array $data): bool
    {
        return ! empty($data['schedules'])
            || ! empty($data['dates'])
            || ! empty($data['date']);
    }

    private function scheduleItems(array $data): array
    {
        if (! empty($data['schedules'])) {
            return $data['schedules'];
        }

        if (! empty($data['dates'])) {
            return array_map(
                fn (string $date) => array_merge(
                    Arr::only($data, ['period_type', 'start_time', 'end_time', 'total_amount', 'offers_meal']),
                    ['date' => $date],
                ),
                $data['dates'],
            );
        }

        return [
            Arr::only($data, ['date', 'period_type', 'start_time', 'end_time', 'total_amount', 'offers_meal']),
        ];
    }

}
