<?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\Schedule;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class ReviewService
{
  public function getAll()
  {
    return Review::with(['schedule.client.user', 'schedule.provider.user', 'reviewsImprovements.improvementType'])
      ->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();
  }

  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 create(array $data): Review
  {
    try {
      DB::beginTransaction();
      $review = new Review();
      $review->fill($data);
      $review->save();
      $review->refresh();

      if (isset($data['origin']) && isset($data['origin_id'])) {
        switch ($data['origin']) {
          case 'client':
            $schedule = Schedule::find($data['schedule_id']);
            $provider = Provider::find($schedule->provider_id);
            $provider->updateAverageRating((float) $data['stars']);
            break;
          case 'provider':
            $schedule = Schedule::find($data['schedule_id']);
            $client = Client::find($schedule->client_id);
            $client->updateAverageRating((float) $data['stars']);
            break;
        }
      }
  
      if (isset($data['improvements_ids'])) {
        $review->improvements()->sync($data['improvements_ids']);
      }
  
      if (!empty($data['block_provider'])) {
        $schedule = Schedule::find($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['block_client'])) {
        $schedule = Schedule::find($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['favorite_provider'])) {
        $schedule = Schedule::find($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,
          ]);
        }
      }

      DB::commit();
      return $review;
    } catch (Exception $e) {
      DB::rollBack();
      Log::error('Error creating review: ' . $e->getMessage());
      throw $e;
    }
  }

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

  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 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 getByProviderSent(int $providerId)
  {
    return Review::with($this->detailEagerLoads())
      ->whereIn('origin', ['provider', 'providers'])
      ->where('origin_id', $providerId)
      ->orderBy('created_at', 'desc')
      ->get();
  }

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

  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();
  }
}
