<?php

namespace App\Services;

use App\Enums\ApprovalStatusEnum;
use App\Enums\UserTypeEnum;
use App\Models\Address;
use App\Models\City;
use App\Models\Provider;
use App\Models\ProviderServicesType;
use App\Models\ProviderWorkingDay;
use App\Models\State;
use App\Models\User;
use App\Services\Pagarme\PagarmeRecipientService;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class ProviderService
{
  public function __construct(
    private readonly AuthService $authService,
    private readonly PagarmeRecipientService $pagarmeRecipientService,
  ) {}

  public function getAll(): Collection
  {
    $providers = Provider::query()
      ->with(['user', 'profileMedia'])
      ->orderBy('created_at', 'desc')
      ->get();

    return $providers;
  }

  public function findById(int $id): ?Provider
  {
    return Provider::with(['user', 'profileMedia'])->find($id);
  }

  public function create(array $data): Provider
  {
    return DB::transaction(function () use ($data) {
      $provider = Provider::create($data);

      if (!empty($data['recipient_code'])) {
        $this->pagarmeRecipientService->createRecipientForProvider($provider, $data);
      }

      return $provider->fresh(['user', 'profileMedia']);
    });
  }

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

    if (!$model) {
      return null;
    }

    $model->update($data);
    return $model->fresh(['user', 'profileMedia']);
  }

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

    if (!$model) {
      return false;
    }

    return $model->delete();
  }

  public function getPending(int $page = 1, int $perPage = 10): LengthAwarePaginator
  {
    return Provider::query()
      ->where('approval_status', ApprovalStatusEnum::PENDING->value)
      ->with(['user', 'profileMedia'])
      ->orderBy('created_at', 'asc')
      ->paginate($perPage, ['*'], 'page', $page);
  }

  public function approve(int $id): Provider
  {
    return DB::transaction(function () use ($id) {
      $provider = Provider::findOrFail($id);
      $provider->update(['approval_status' => ApprovalStatusEnum::ACCEPTED->value]);
      return $provider->fresh(['user', 'profileMedia']);
    });
  }

  public function reject(int $id): Provider
  {
    return DB::transaction(function () use ($id) {
      $provider = Provider::findOrFail($id);
      $provider->update(['approval_status' => ApprovalStatusEnum::REJECTED->value]);
      return $provider->fresh(['user', 'profileMedia']);
    });
  }

  public function register(array $data): ?array
  {
    try {
      DB::beginTransaction();

      $email = $data['email'] ?? null;
      $phone = $data['phone'] ?? null;
      $code = $data['code'] ?? null;

      $user = User::query()
        ->where('type', UserTypeEnum::PROVIDER->value)
        ->where('code', $code)
        ->where(function ($query) use ($email, $phone) {
          if (!empty($email)) {
            $query->orWhere('email', $email);
          }

          if (!empty($phone)) {
            $query->orWhere('phone', $phone);
          }
        })
        ->latest('id')
        ->first();

      if (!$user) {
        throw new \Exception(__('messages.user_not_found_or_code_not_validated'));
      }

      $user->name = $data['name'];

      if (empty($user->email) && !empty($email)) {
        $user->email = $email;
      }

      if (empty($user->phone) && !empty($phone)) {
        $user->phone = $phone;
      }

      $user->save();

      $provider = new Provider();
      $provider->user_id = $user->id;
      $provider->rg = $data['rg'] ?? null;
      $provider->document = $this->sanitizeDigits($data['document'] ?? null);
      $provider->birth_date = $data['birth_date'] ?? null;
      $provider->daily_price_8h = $data['daily_price_8h'] ?? null;
      $provider->daily_price_6h = $data['daily_price_6h'] ?? null;
      $provider->daily_price_4h = $data['daily_price_4h'] ?? null;
      $provider->daily_price_2h = $data['daily_price_2h'] ?? null;
      $provider->approval_status = ApprovalStatusEnum::PENDING->value;
      $provider->selfie_media_base64 = $data['selfie_base64'] ?? null;
      $provider->document_front_media_base64 = $data['document_front_base64'] ?? null;
      $provider->document_back_media_base64 = $data['document_back_base64'] ?? null;
      $provider->save();
      $provider->refresh();

      $this->pagarmeRecipientService->createRecipientForProvider($provider, $data);
      $this->createProviderAddress($provider->id, $data);
      $this->createProviderServicesTypes($provider->id, $data);
      $this->createProviderWorkingDays($provider->id, $data);

      if (empty($user->email) || empty($user->code)) {
        throw new \Exception(__('messages.user_not_found_or_code_not_validated'));
      }

      $result = $this->authService->loginWithEmail(
        email: $user->email,
        code: $user->code,
      );

      DB::commit();
      return $result;
    } catch (\Exception $e) {
      DB::rollBack();
      Log::error('Error registering provider: ' . $e->getMessage(), [
        'data' => $data,
      ]);
      throw $e;
    }
  }

  private function createProviderAddress(int $providerId, array $data): void
  {
    $state = null;
    $city = null;

    if (!empty($data['state'])) {
      $state = State::query()
        ->whereRaw('LOWER(code) = ?', [mb_strtolower($data['state'])])
        ->first();
    }

    if (!empty($data['city'])) {
      $cityQuery = City::query()
        ->whereRaw('LOWER(name) = ?', [mb_strtolower($data['city'])]);

      if ($state) {
        $cityQuery->where('state_id', $state->id);
      }

      $city = $cityQuery->first();
    }

    $address = new Address();
    $address->source = 'provider';
    $address->source_id = $providerId;
    $address->zip_code = $this->sanitizeDigits($data['zip_code'] ?? null);
    $address->address = $data['address'] ?? null;
    $address->has_complement = (bool) ($data['has_complement'] ?? false);
    $address->complement = $data['complement'] ?? null;
    $address->nickname = $data['nickname'] ?? null;
    $address->instructions = $data['instructions'] ?? null;
    $address->address_type = $data['address_type'] ?? 'home';
    $address->state_id = $state?->id;
    $address->city_id = $city?->id;
    $address->save();
  }

  private function createProviderServicesTypes(int $providerId, array $data): void
  {
    $serviceTypeIds = $data['services_types_ids'] ?? $data['service_types_ids'] ?? [];

    $uniqueIds = array_values(array_unique(array_map('intval', $serviceTypeIds)));

    foreach ($uniqueIds as $serviceTypeId) {
      ProviderServicesType::create([
        'provider_id' => $providerId,
        'service_type_id' => $serviceTypeId,
      ]);
    }
  }

  private function createProviderWorkingDays(int $providerId, array $data): void
  {
    $workingDays = $data['working_days'] ?? [];
    $seen = [];

    foreach ($workingDays as $workingDay) {
      $day = (int) ($workingDay['day'] ?? -1);
      $period = $workingDay['period'] ?? null;

      if ($day < 0 || $day > 6 || !in_array($period, ['morning', 'afternoon'], true)) {
        continue;
      }

      $uniqueKey = $day . '-' . $period;
      if (isset($seen[$uniqueKey])) {
        continue;
      }

      $seen[$uniqueKey] = true;

      ProviderWorkingDay::create([
        'provider_id' => $providerId,
        'day' => $day,
        'period' => $period,
      ]);
    }
  }

  private function sanitizeDigits(?string $value): ?string
  {
    if ($value === null) {
      return null;
    }

    $digits = preg_replace('/\D+/', '', $value);

    return $digits === '' ? null : $digits;
  }
}
