<?php

namespace App\Models\Person;

use App\Traits\Uuid;
use Illuminate\Database\Eloquent\Builder;
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\Str;
use Laravel\Sanctum\HasApiTokens;
use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Searchable;

class Person extends Model
{
    use HasApiTokens;

    /** @use HasFactory<\Database\Factories\Person/PersonFactory> */
    use HasFactory;

    use Searchable;
    use SoftDeletes;
    use Uuid;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $table = 'person';

    protected $fillable = [
        'nu',
        'first_name',
        'last_name',
        'full_name',
        'gender',
        'birthday',
        'tax_number',
        'social_security_number',
        'description',
        'group_id',
        'type_id',
        'user_id',
        'employer'
    ];

    protected $hidden = [
        'id',
        'deleted',
        'user_id',
    ];

    protected static function booted()
    {
        static::creating(function (Person $person) {
            if (! isset($person->user_id)) {
                $person->user_id = auth()->id();
            }
            // Ensure a unique 6-character alphanumeric 'nu' is set globally on create
            if (empty($person->nu)) {
                $person->nu = static::generateUniqueNu();
            }
        });

        static::saving(function (Person $person) {
            $person->full_name_search = static::buildFullNameSearchPayload(
                $person->first_name,
                $person->last_name,
                $person->full_name
            );
        });
    }

    protected function makeAllSearchableUsing(Builder $query): Builder
    {
        return $query->with(['addresses', 'phones', 'emails']);
    }

    #[SearchUsingFullText(['full_name_search'], ['config' => 'simple'])]
    public function toSearchableArray(): array
    {
        $columns = [
            'first_name' => (string) $this->first_name,
            'last_name' => (string) $this->last_name,
            'full_name' => (string) $this->full_name,
            'person_addresses.address' => '',
            'person_phones.nu' => '',
            'emails.value' => '',
            'full_name_search' => (string) $this->full_name_search,
        ];

        return $columns;
    }

    public function phones(): HasMany
    {
        return $this->hasMany(\App\Models\Person\PersonPhone::class)
            ->with(['type'])
            ->where('active', '=', 1)
            ->orderBy('id');
    }

    public function addresses(): HasMany
    {
        return $this->hasMany(\App\Models\Person\PersonAddress::class)
            ->with(['type'])
            ->where('active', '=', 1)
            ->orderBy('id');
    }

    public function address(): HasOne
    {
        return $this->hasOne(\App\Models\Person\PersonAddress::class)
            ->with(['type'])
            ->where('active', '=', 1)
            ->oldestOfMany('id');
    }

    public function emails(): HasMany
    {
        return $this->hasMany(\App\Models\Email::class, 'person_id')
            ->where('is_active', '=', 1)
            ->orderBy('id');
    }

    public function bankAccounts(): HasMany
    {
        return $this->hasMany(\App\Models\BankAccount::class, 'person_id')
            ->where('is_active', '=', 1)
            ->orderBy('id');
    }

    public function group(): BelongsTo
    {
        return $this->belongsTo(\App\Models\Person\PersonGroup::class, 'group_id');
    }

    public function type(): BelongsTo
    {
        return $this->belongsTo(\App\Models\Person\PersonType::class, 'type_id');
    }

    public function client(): HasOne
    {
        return $this->hasOne(\App\Models\Client::class);
    }

    public function clientCase(): HasOne
    {
        return $this->hasOne(\App\Models\ClientCase::class);
    }

    /**
     * Generate a unique 6-character alphanumeric identifier for 'nu'.
     */
    protected static function generateUniqueNu(): string
    {
        do {
            $nu = Str::random(6); // [A-Za-z0-9]
        } while (static::where('nu', $nu)->exists());

        return $nu;
    }

    protected static function buildFullNameSearchPayload(?string $firstName, ?string $lastName, ?string $fullName): string
    {
        $segments = collect([
            static::joinNameParts($firstName, $lastName),
            static::joinNameParts($lastName, $firstName),
            $fullName,
        ])->filter();

        if ($segments->isEmpty()) {
            return '';
        }

        return $segments
            ->map(fn (string $segment): string => static::normalizeSegment($segment))
            ->filter()
            ->unique()
            ->implode(' ');
    }

    protected static function joinNameParts(?string $first, ?string $second): ?string
    {
        $parts = collect([$first, $second])->filter(fn ($value) => filled($value));

        if ($parts->isEmpty()) {
            return null;
        }

        return $parts->implode(' ');
    }

    protected static function normalizeSegment(?string $value): ?string
    {
        if (empty($value)) {
            return null;
        }

        return (string) Str::of($value)->squish()->lower();
    }
}
