<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Models\UserSetting;
use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable;
    use HasUlids; // generates ULIDs automatically

    protected $keyType = 'string';
    public $incrementing = false;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'firstname',
        'lastname',
        'displayname',
        'name',
        'email',
        'timezone',
        'phone',
        'locale',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }

    /**
     * Expose a Breeze-compatible "name" attribute without a physical column.
     * Preference: displayname (explicit override), then first + last, then email.
     */
    public function getNameAttribute(): string
    {
        $displayname = is_string($this->displayname) ? trim($this->displayname) : '';
        if ($displayname !== '') {
            return $displayname;
        }

        $first = is_string($this->firstname) ? trim($this->firstname) : '';
        $last = is_string($this->lastname) ? trim($this->lastname) : '';
        $full = trim($first . ' ' . $last);

        if ($full !== '') {
            return $full;
        }

        return (string) ($this->email ?? '');
    }

    /**
     * Map "name" writes to first/last names, keeping displayname optional.
     */
    public function setNameAttribute(?string $value): void
    {
        $incoming = trim((string) $value);

        $currentFirst = is_string($this->attributes['firstname'] ?? null)
            ? trim((string) $this->attributes['firstname'])
            : '';
        $currentLast = is_string($this->attributes['lastname'] ?? null)
            ? trim((string) $this->attributes['lastname'])
            : '';
        $currentGenerated = trim($currentFirst . ' ' . $currentLast);

        if ($incoming === '') {
            $this->attributes['firstname'] = null;
            $this->attributes['lastname'] = null;
            return;
        }

        $parts = preg_split('/\s+/', $incoming, 2);
        $this->attributes['firstname'] = $parts[0] ?? null;
        $this->attributes['lastname'] = $parts[1] ?? null;

        $displayname = is_string($this->attributes['displayname'] ?? null)
            ? trim((string) $this->attributes['displayname'])
            : '';

        if ($displayname !== '' && $displayname === $currentGenerated) {
            $this->attributes['displayname'] = $incoming;
        }
    }

    /**
     * user can own many calendars
     */
    public function calendars(): HasMany
    {
        return $this->hasMany(Calendar::class);
    }

    /**
     * get the current user's principal uri
     */
    public function getPrincipalUriAttribute(): string
    {
        return 'principals/' . $this->email;
    }

    /**
     * get all user addresses
     */
    public function addresses()
    {
        return $this->hasMany(\App\Models\UserAddress::class, 'user_id', 'id');
    }

    /**
     * get the user's billing address
     */
    public function billingAddress()
    {
        return $this->addresses()
            ->where('kind', 'billing')
            ->where('is_primary', 1)
            ->first();
    }

    /**
     * user can have many settings
     */
    public function userSettings(): HasMany
    {
        return $this->hasMany(UserSetting::class, 'user_id');
    }

    public function eventAttendees(): HasMany
    {
        return $this->hasMany(EventAttendee::class, 'attendee_user_id');
    }

    /**
     * get a user setting by key
     */
    public function getSetting(string $key, mixed $default = null): mixed
    {
        // avoid repeated queries if the relationship is already eager loaded
        if ($this->relationLoaded('userSettings')) {
            $match = $this->userSettings->firstWhere('key', $key);
            return $match?->value ?? $default;
        }

        $value = $this->userSettings()
            ->where('key', $key)
            ->value('value');

        return $value ?? $default;
    }

    /**
     * set a user setting by key
     */
    public function setSetting(string $key, mixed $value): void
    {
        // store everything as a string in the value column
        $stringValue = is_null($value) ? null : (string) $value;

        $this->userSettings()->updateOrCreate(
            ['key' => $key],
            ['value' => $stringValue],
        );

        // keep in-memory relation in sync if it was loaded
        if ($this->relationLoaded('userSettings')) {
            $existing = $this->userSettings->firstWhere('key', $key);

            if ($existing) {
                $existing->value = $stringValue;
            } else {
                $this->userSettings->push(new UserSetting([
                    'user_id' => $this->getKey(),
                    'key' => $key,
                    'value' => $stringValue,
                ]));
            }
        }
    }

    /**
     * convenience: set many settings at once
     */
    public function setSettings(array $pairs): void
    {
        foreach ($pairs as $key => $value) {
            $this->setSetting($key, $value);
        }
    }
}
