<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use Lab404\Impersonate\Models\Impersonate;
use Spatie\Permission\Traits\HasRoles;

final class User extends Authenticatable implements MustVerifyEmail
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, HasRoles, Impersonate, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'phone',
        'profile_picture',
        'timezone',
        'status',
        'partner_tier',
        'total_invested',
        'total_returns',
        'partner_since',
        'address_line_1',
        'address_line_2',
        'city',
        'state',
        'country',
        'postal_code',
    ];

    /**
     * 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',
            'phone_verified_at' => 'datetime',
            'password' => 'hashed',
            'timezone' => 'string',
            'status' => 'string',
            'total_invested' => 'decimal:2',
            'total_returns' => 'decimal:2',
            'partner_since' => 'datetime',
            'profile_picture' => 'string',
        ];
    }

    protected function profilePictureUrl(): Attribute
    {
        return Attribute::make(
            get: fn () => filled($this->profile_picture)
                ? (str_starts_with($this->profile_picture, 'http')
                    ? $this->profile_picture
                    : '/storage/'.ltrim($this->profile_picture, '/'))
                : null
        );
    }

    /**
     * Get the user's initials
     */
    public function initials(): string
    {
        return Str::of($this->name)
            ->explode(' ')
            ->take(2)
            ->map(fn ($word) => Str::substr($word, 0, 1))
            ->implode('');
    }

    /**
     * Get all properties owned by this user
     */
    public function properties(): HasMany
    {
        return $this->hasMany(Property::class, 'owner_id');
    }

    /**
     * Get all bookings made by this user (as a guest)
     */
    public function bookings(): HasMany
    {
        return $this->hasMany(Booking::class, 'guest_id');
    }

    public function addresses(): HasMany
    {
        return $this->hasMany(Address::class);
    }

    public function orders(): HasMany
    {
        return $this->hasMany(Order::class);
    }

    /**
     * Get all reviews written by this user (as a guest)
     */
    public function reviews(): HasMany
    {
        return $this->hasMany(Review::class, 'guest_id');
    }

    /**
     * @return HasMany<BlogPostReview, $this>
     */
    public function blogPostReviews(): HasMany
    {
        return $this->hasMany(BlogPostReview::class, 'user_id');
    }

    /**
     * Get all support tickets raised by this user.
     */
    public function supportTickets(): HasMany
    {
        return $this->hasMany(SupportTicket::class);
    }

    /**
     * Get all payment accounts for this user (as an owner)
     */
    public function paymentAccounts(): HasMany
    {
        return $this->hasMany(OwnerPaymentAccount::class, 'owner_id');
    }

    /**
     * Get all favorite properties for this user
     */
    public function favorites(): BelongsToMany
    {
        return $this->belongsToMany(Property::class, 'favorites')
            ->withTimestamps();
    }

    /**
     * Check if user has favorited a property
     */
    public function hasFavorited(int $propertyId): bool
    {
        return $this->favorites()->where('property_id', $propertyId)->exists();
    }

    /**
     * Get all conversations where user is the guest
     */
    public function guestConversations(): HasMany
    {
        return $this->hasMany(Conversation::class, 'guest_id');
    }

    /**
     * Get all conversations where user is the owner
     */
    public function ownerConversations(): HasMany
    {
        return $this->hasMany(Conversation::class, 'owner_id');
    }

    /**
     * Get all messages sent by this user
     */
    public function messages(): HasMany
    {
        return $this->hasMany(Message::class, 'sender_id');
    }

    /**
     * Get all linked social provider accounts.
     */
    public function socialAccounts(): HasMany
    {
        return $this->hasMany(SocialAccount::class);
    }

    public function canImpersonate(): bool
    {
        return $this->hasRole('admin');
    }

    public function canBeImpersonated(): bool
    {
        return $this->status === 'active' && ! $this->hasRole('admin');
    }

    /**
     * Get all activities performed by this user
     */
    public function activities(): MorphMany
    {
        return $this->morphMany(\Spatie\Activitylog\Models\Activity::class, 'causer');
    }

    /**
     * Get partner balance (for partners)
     */
    public function partnerBalance(): HasOne
    {
        return $this->hasOne(PartnerBalance::class, 'partner_id');
    }

    /**
     * Get all partner investments (for partners)
     */
    public function partnerInvestments(): HasMany
    {
        return $this->hasMany(PartnerInvestment::class, 'partner_id');
    }

    /**
     * Get all partner distributions (for partners)
     */
    public function partnerDistributions(): HasMany
    {
        return $this->hasMany(PartnerDistribution::class, 'partner_id');
    }

    /**
     * Get all partner balance transactions (for partners)
     */
    public function partnerBalanceTransactions(): HasMany
    {
        return $this->hasMany(PartnerBalanceTransaction::class, 'partner_id');
    }

    /**
     * Alias for partnerBalance
     */
    public function balance(): HasOne
    {
        return $this->partnerBalance();
    }

    /**
     * Alias for partnerInvestments
     */
    public function investments(): HasMany
    {
        return $this->partnerInvestments();
    }

    /**
     * Alias for partnerDistributions
     */
    public function distributions(): HasMany
    {
        return $this->partnerDistributions();
    }

    /**
     * Alias for partnerBalanceTransactions
     */
    public function balanceTransactions(): HasMany
    {
        return $this->partnerBalanceTransactions();
    }
}
