<?php

namespace App\Services;

use App\Models\Client;
use App\Models\ClientFavoriteProvider;
use App\Models\ClientProviderBlock;
use App\Models\Provider;
use App\Models\ProviderClientBlock;
use App\Models\Review;
use App\Models\ReviewMedia;
use App\Models\Schedule;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;

class ReviewService
{
    public function __construct(private readonly MediaService $mediaService) {}

    public function getAll()
    {
        return Review::with(['schedule.client.user', 'schedule.provider.user', 'reviewsImprovements.improvementType'])
            ->orderBy('created_at', 'desc')
            ->get();
    }

    public function create(array $data): Review
    {
        try {
            DB::beginTransaction();

            $review = new Review();

            $review->fill($data);
            $review->save();
            $review->refresh();

            if (data_get($data, 'origin') !== null && data_get($data, 'origin_id') !== null) {
                switch (data_get($data, 'origin')) {
                    case 'client':
                        $schedule = Schedule::find(data_get($data, 'schedule_id'));
                        $provider = Provider::find($schedule->provider_id);

                        $provider->updateAverageRating((float) data_get($data, 'stars'));

                        break;

                    case 'provider':
                        $schedule = Schedule::find(data_get($data, 'schedule_id'));
                        $client   = Client::find($schedule->client_id);

                        $client->updateAverageRating((float) data_get($data, 'stars'));

                        break;
                }
            }

            if (data_get($data, 'improvements_ids') !== null) {
                $review->improvements()->sync(data_get($data, 'improvements_ids'));
            }

            if (! empty(data_get($data, 'block_provider'))) {
                $schedule = Schedule::find(data_get($data, 'schedule_id'));

                $alreadyBlocked = ClientProviderBlock::where('client_id', $schedule->client_id)
                    ->where('provider_id', $schedule->provider_id)
                    ->whereNull('deleted_at')
                    ->exists();

                if (! $alreadyBlocked) {
                    ClientProviderBlock::create([
                        'client_id'   => $schedule->client_id,
                        'provider_id' => $schedule->provider_id,
                    ]);
                }
            }

            if (! empty(data_get($data, 'block_client'))) {
                $schedule = Schedule::find(data_get($data, 'schedule_id'));

                $alreadyBlocked = ProviderClientBlock::where('provider_id', $schedule->provider_id)
                    ->where('client_id', $schedule->client_id)
                    ->whereNull('deleted_at')
                    ->exists();

                if (! $alreadyBlocked) {
                    ProviderClientBlock::create([
                        'provider_id' => $schedule->provider_id,
                        'client_id'   => $schedule->client_id,
                    ]);
                }
            }

            if (! empty(data_get($data, 'favorite_provider'))) {
                $schedule = Schedule::find(data_get($data, 'schedule_id'));

                $alreadyFavorited = ClientFavoriteProvider::where('client_id', $schedule->client_id)
                    ->where('provider_id', $schedule->provider_id)
                    ->whereNull('deleted_at')
                    ->exists();

                if (! $alreadyFavorited) {
                    ClientFavoriteProvider::create([
                        'client_id'   => $schedule->client_id,
                        'provider_id' => $schedule->provider_id,
                    ]);
                }
            }

            if (! empty(data_get($data, 'photos'))) {
                $schedule = Schedule::find(data_get($data, 'schedule_id'));

                $origin = data_get($data, 'origin');

                foreach (data_get($data, 'photos') as $photo) {
                    $media = $this->mediaService->createFromFile(
                        file:     $photo,
                        folder:   "review/{$schedule->id}/{$origin}",
                        source:   'review',
                        sourceId: $review->id,
                    );

                    ReviewMedia::create([
                        'review_id' => $review->id,
                        'media_id'  => $media->id,
                        'origin'    => $origin,
                    ]);
                }
            }

            DB::commit();

            return $review;
        } catch (Exception $e) {
            DB::rollBack();

            Log::error('Erro ao criar avaliação: '.$e->getMessage());

            throw $e;
        }
    }

    public function update(int $id, array $data): Review
    {
        $review = Review::findOrFail($id);

        $review->update($data);

        return $review->fresh();
    }

    public function delete(int $id): bool
    {
        $review = Review::findOrFail($id);

        return $review->delete();
    }

    //

    public function getByClientReceived(int $clientId)
    {
        return Review::with($this->detailEagerLoads())
            ->whereIn('origin', ['provider', 'providers'])
            ->whereHas('schedule', fn ($q) => $q->where('client_id', $clientId))
            ->orderBy('created_at', 'desc')
            ->get();
    }

    public function getByClientSent(int $clientId)
    {
        return Review::with($this->detailEagerLoads())
            ->whereIn('origin', ['client', 'clients'])
            ->where('origin_id', $clientId)
            ->orderBy('created_at', 'desc')
            ->get();
    }

    public function getByProviderReceived(int $providerId)
    {
        $reviews = Review::with($this->detailEagerLoads())
            ->whereIn('origin', ['client', 'clients'])
            ->whereHas('schedule', fn ($q) => $q->where('provider_id', $providerId))
            ->orderBy('created_at', 'desc')
            ->get();

        foreach ($reviews as $review) {
            if ($review->schedule?->client?->profileMedia?->path) {
                $review->schedule->client->profile_photo = Storage::temporaryUrl($review->schedule->client->profileMedia->path, now()->addMinutes(60));
            } else {
                $review->schedule->client->profile_photo = null;
            }
        }

        return $reviews;
    }

    public function getByProviderSent(int $providerId)
    {
        return Review::with($this->detailEagerLoads())
            ->whereIn('origin', ['provider', 'providers'])
            ->where('origin_id', $providerId)
            ->orderBy('created_at', 'desc')
            ->get();
    }

    //

    public function getByOrigin(string $origin, int $originId)
    {
        return Review::with(['schedule.client.user', 'schedule.provider.user'])
            ->where('origin', $origin)
            ->where('origin_id', $originId)
            ->orderBy('created_at', 'desc')
            ->get();
    }

    public function getByScheduleId(int $scheduleId)
    {
        return Review::with(['schedule.client.user', 'schedule.provider.user'])
            ->where('schedule_id', $scheduleId)
            ->orderBy('created_at', 'desc')
            ->get();
    }

    //

    private function detailEagerLoads(): array
    {
        return [
            'schedule.client.user',
            'schedule.client.profileMedia',
            'schedule.provider.user',
            'schedule.address.city',
            'schedule.address.state',
            'schedule.customSchedule.serviceType',
            'schedule.customSchedule.specialities',
            'improvements',
        ];
    }
}
