<?php

namespace App\Services;

use App\Enums\ServicePackageStatusEnum;
use App\Enums\PaymentStatusEnum;
use App\Models\ServicePackage;
use App\Models\ServicePackageItem;
use App\Models\Payment;
use App\Models\Schedule;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class ServicePackageItemService
{
    public function getAll(): Collection
    {
        return ServicePackageItem::query()
            ->orderBy('created_at', 'desc')
            ->get();
    }

    public function findById(int $id): ?ServicePackageItem
    {
        return ServicePackageItem::find($id);
    }

    public function create(array $data): ServicePackageItem
    {
        return DB::transaction(function () use ($data) {
            [$servicePackage, $schedule] = $this->validateItem(data_get($data, 'service_package_id'), data_get($data, 'schedule_id'));

            if (! $servicePackage->provider_id) {
                $servicePackage->update(['provider_id' => $schedule->provider_id]);
            }

            return ServicePackageItem::create($data);
        });
    }

    public function update(int $id, array $data): ?ServicePackageItem
    {
        return DB::transaction(function () use ($id, $data): ?ServicePackageItem {
            $model = $this->findById($id);

            if (! $model) {
                return null;
            }

            $servicePackageId = data_get($data, 'service_package_id', $model->service_package_id);

            $scheduleId = data_get($data, 'schedule_id', $model->schedule_id);

            [$servicePackage, $schedule] = $this->validateItem($servicePackageId, $scheduleId);

            if (! $servicePackage->provider_id) {
                $servicePackage->update(['provider_id' => $schedule->provider_id]);
            }

            $model->update($data);

            return $model->fresh();
        });
    }

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

        if (!$model) {
            return false;
        }

        return DB::transaction(function () use ($model): bool {
            $this->validateItem($model->service_package_id, $model->schedule_id);

            return $model->delete();
        });
    }

    private function validateItem(int $servicePackageId, int $scheduleId): array
    {
        $servicePackage = ServicePackage::query()->lockForUpdate()->findOrFail($servicePackageId);
        $schedule = Schedule::query()->findOrFail($scheduleId);

        if ($servicePackage->status !== ServicePackageStatusEnum::OPEN) {
            throw ValidationException::withMessages([
                'service_package_id' => __('messages.service_package_must_be_open_for_changes'),
            ]);
        }

        if ($schedule->client_id !== $servicePackage->client_id) {
            throw ValidationException::withMessages([
                'schedule_id' => __('messages.service_package_schedule_same_client'),
            ]);
        }

        if (! $schedule->provider_id || ($servicePackage->provider_id && $schedule->provider_id !== $servicePackage->provider_id)) {
            throw ValidationException::withMessages([
                'schedule_id' => __('messages.service_package_same_provider'),
            ]);
        }

        $belongsToAnotherPackage = ServicePackageItem::query()
            ->where('schedule_id', $scheduleId)
            ->where('service_package_id', '!=', $servicePackageId)
            ->exists();

        if ($belongsToAnotherPackage) {
            throw ValidationException::withMessages([
                'schedule_id' => __('messages.service_package_schedule_already_assigned'),
            ]);
        }

        $hasActivePayment = Payment::query()
            ->where('schedule_id', $scheduleId)
            ->whereIn('status', [
                PaymentStatusEnum::PENDING->value,
                PaymentStatusEnum::PROCESSING->value,
                PaymentStatusEnum::AUTHORIZED->value,
                PaymentStatusEnum::PAID->value,
            ])
            ->exists();

        if ($hasActivePayment) {
            throw ValidationException::withMessages([
                'schedule_id' => __('messages.service_package_schedule_has_active_payment'),
            ]);
        }

        return [$servicePackage, $schedule];
    }
}
