<?php

namespace App\Models;

use App\Enums\ApprovalStatusEnum;
use App\Enums\GenderEnum;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Auth;

/**
 * @property int $id
 * @property string $document
 * @property string|null $rg
 * @property int $user_id
 * @property numeric|null $average_rating
 * @property int $total_services
 * @property \Illuminate\Support\Carbon|null $birth_date
 * @property GenderEnum|null $gender
 * @property bool $selfie_verified
 * @property bool $document_verified
 * @property numeric|null $daily_price_8h
 * @property numeric|null $daily_price_6h
 * @property numeric|null $daily_price_4h
 * @property numeric|null $daily_price_2h
 * @property int|null $profile_media_id
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 * @property \Illuminate\Support\Carbon|null $deleted_at
 * @property ApprovalStatusEnum $approval_status
 * @property string|null $recipient_id
 * @property string|null $recipient_name
 * @property string|null $recipient_email
 * @property string|null $recipient_description
 * @property string|null $recipient_document
 * @property string|null $recipient_type
 * @property string|null $recipient_code
 * @property string|null $recipient_payment_mode
 * @property array<array-key, mixed>|null $recipient_default_bank_account
 * @property array<array-key, mixed>|null $recipient_transfer_settings
 * @property array<array-key, mixed>|null $recipient_automatic_anticipation_settings
 * @property array<array-key, mixed>|null $recipient_metadata
 * @property int|null $document_front_media_id
 * @property int|null $document_back_media_id
 * @property string|null $idempotency_key
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Address> $addresses
 * @property-read int|null $addresses_count
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ClientProviderBlock> $blockedByClients
 * @property-read int|null $blocked_by_clients_count
 * @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ProviderClientBlock> $blockedClients
 * @property-read int|null $blocked_clients_count
 * @property-read \App\Models\Media|null $documentBackMedia
 * @property-read \App\Models\Media|null $documentFrontMedia
 * @property-read \App\Models\Address|null $primaryAddress
 * @property-read \App\Models\Media|null $profileMedia
 * @property-read \App\Models\User $user
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider newModelQuery()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider newQuery()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider onlyTrashed()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider query()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider visibleToCustomers()
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereApprovalStatus($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereAverageRating($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereBirthDate($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereCreatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDailyPrice2h($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDailyPrice4h($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDailyPrice6h($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDailyPrice8h($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDeletedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDocument($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDocumentBackMediaId($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDocumentFrontMediaId($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereDocumentVerified($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereId($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereIdempotencyKey($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereProfileMediaId($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientAutomaticAnticipationSettings($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientCode($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientDefaultBankAccount($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientDescription($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientDocument($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientEmail($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientId($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientMetadata($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientName($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientPaymentMode($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientTransferSettings($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRecipientType($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereRg($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereSelfieVerified($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereTotalServices($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereUpdatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider whereUserId($value)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider withTrashed(bool $withTrashed = true)
 * @method static \Illuminate\Database\Eloquent\Builder<static>|Provider withoutTrashed()
 * @mixin \Eloquent
 */
class Provider extends Model
{
    use HasFactory, SoftDeletes;

    protected $table = "providers";

    protected $guarded = ["id", "recipient_code"];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'birth_date'                                => 'date',
            'gender'                                    => GenderEnum::class,
            'selfie_verified'                           => 'boolean',
            'document_verified'                         => 'boolean',
            'approval_status'                           => ApprovalStatusEnum::class,
            'average_rating'                            => 'decimal:1',
            'daily_price_8h'                            => 'decimal:2',
            'daily_price_6h'                            => 'decimal:2',
            'daily_price_4h'                            => 'decimal:2',
            'daily_price_2h'                            => 'decimal:2',
            'total_services'                            => 'integer',
            'recipient_default_bank_account'            => 'array',
            'recipient_transfer_settings'               => 'array',
            'recipient_automatic_anticipation_settings' => 'array',
            'recipient_metadata'                        => 'array',
        ];
    }

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    //

    public function documentFrontMedia(): BelongsTo
    {
        return $this->belongsTo(Media::class, 'document_front_media_id');
    }

    public function documentBackMedia(): BelongsTo
    {
        return $this->belongsTo(Media::class, 'document_back_media_id');
    }

    public function profileMedia(): BelongsTo
    {
        $authenticatedUser = Auth::user();

        $relation = $this->belongsTo(Media::class, 'profile_media_id')
            ->join('providers as profile_media_providers', function ($join) {
                $join->on('profile_media_providers.id', '=', 'media.source_id')
                    ->on('profile_media_providers.profile_media_id', '=', 'media.id');
            })
            ->where('media.source', 'provider')
            ->select('media.*');

        if ($authenticatedUser?->canModerateProviders()) {
            return $relation;
        }

        return $relation->where(function ($query) use ($authenticatedUser) {
            $query->where('profile_media_providers.selfie_verified', true);

            if ($authenticatedUser !== null) {
                $query->orWhere('profile_media_providers.user_id', $authenticatedUser->id);
            }
        });
    }

    //

    public function addresses(): HasMany
    {
        return $this->hasMany(Address::class, 'source_id')
            ->where('source', 'provider');
    }

    public function primaryAddress()
    {
        return $this->hasOne(Address::class, "source_id")
            ->where("source", "provider")
            ->orderBy("is_primary", "desc");
    }

    //

    public function bankAccounts(): HasMany
    {
        return $this->hasMany(ProviderBankAccount::class)
            ->orderBy('is_primary', 'desc')
            ->orderBy('created_at', 'asc');
    }

    public function primaryBankAccount(): HasOne
    {
        return $this->hasOne(ProviderBankAccount::class)
            ->where('is_primary', true);
    }

    //

    public function blockedByClients()
    {
        return $this->hasMany(ClientProviderBlock::class);
    }

    public function blockedClients()
    {
        return $this->hasMany(ProviderClientBlock::class);
    }

    //

    public function updateAverageRating(float $newRating): void
    {
        $totalReviews = Review::where('reviews.origin', 'client')
            ->leftJoin('schedules', 'schedules.id', '=', 'reviews.schedule_id')
            ->where('schedules.provider_id', $this->id)
            ->count();

        if ($totalReviews === 0) {
            $this->average_rating = $newRating;
        } else {
            $currentTotalRating = $this->average_rating * ($totalReviews - 1);

            $newAverage = ($currentTotalRating + $newRating) / $totalReviews;

            $this->average_rating = round($newAverage, 2);
        }

        $this->save();
    }

    //

    public function ensureGatewayCode(): string
    {
        if (! empty($this->recipient_code)) {
            return $this->recipient_code;
        }

        $code = 'provider-'.(string) \Illuminate\Support\Str::uuid();

        $this->forceFill(['recipient_code' => $code])->save();

        return $code;
    }

    public function scopeVisibleToCustomers($query)
    {
        return $query->whereExists(self::hasActivePrimaryBankAccount());
    }

    /**
     * Prestador so aparece para os clientes quando tem conta bancaria principal ativa.
     *
     * Devolvido como closure para poder ser usado tambem em queries que chegam em
     * providers por join (favoritos, propostas), e nao apenas pelo model.
     */
    public static function hasActivePrimaryBankAccount(): \Closure
    {
        return fn ($query) => $query
            ->select(\Illuminate\Support\Facades\DB::raw(1))
            ->from('provider_bank_accounts')
            ->whereColumn('provider_bank_accounts.provider_id', 'providers.id')
            ->where('provider_bank_accounts.is_primary', true)
            ->where('provider_bank_accounts.is_active', true)
            ->whereNull('provider_bank_accounts.deleted_at');
    }
}
