<?php

declare(strict_types=1);

namespace App\Models;

use App\Enums\Gender;
use App\Enums\HouseholdRole;
use App\Enums\MaritalStatus;
use App\Enums\MembershipStatus;
use App\Models\Concerns\BelongsToBranch;
use Database\Factories\MemberFactory;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;

class Member extends Model
{
    /** @use HasFactory<MemberFactory> */
    use BelongsToBranch, HasFactory, SoftDeletes;

    /**
     * Transient, non-persisted — lets the status-change endpoint pass a
     * reason/effective_date through to MemberObserver::updated() without
     * a dedicated write path around the generic update().
     */
    public ?string $pendingStatusReason = null;

    public ?string $pendingStatusEffectiveDate = null;

    protected $fillable = [
        'branch_id',
        'household_id',
        'household_role',
        'title',
        'first_name',
        'middle_name',
        'last_name',
        'preferred_name',
        'gender',
        'date_of_birth',
        'marital_status',
        'email',
        'phone_primary',
        'phone_secondary',
        'address_line1',
        'address_line2',
        'city',
        'state_province',
        'postal_code',
        'country',
        'photo_path',
        'membership_status',
        'date_joined',
        'date_baptized',
        'baptism_type',
        'occupation',
        'employer',
        'emergency_contact_name',
        'emergency_contact_phone',
        'emergency_contact_relationship',
        'custom_fields',
        'created_by',
        'updated_by',
    ];

    protected function casts(): array
    {
        return [
            'household_role' => HouseholdRole::class,
            'gender' => Gender::class,
            'date_of_birth' => 'date',
            'marital_status' => MaritalStatus::class,
            'membership_status' => MembershipStatus::class,
            'date_joined' => 'date',
            'date_baptized' => 'date',
            'custom_fields' => 'array',
        ];
    }

    /**
     * Cross-branch reads must 404, not 403 (never leak existence across
     * branches) — scoping the binding itself is simpler and safer than
     * remembering to call visibleTo() in every controller method.
     */
    public function resolveRouteBinding($value, $field = null)
    {
        $query = static::query();
        $user = auth()->user();

        if ($user) {
            $query->visibleTo($user);
        }

        return $query->where($field ?? $this->getRouteKeyName(), $value)->first();
    }

    protected function phonePrimary(): Attribute
    {
        return Attribute::make(
            set: fn (?string $value) => $value !== null ? preg_replace('/\D+/', '', $value) : null,
        );
    }

    protected function phoneSecondary(): Attribute
    {
        return Attribute::make(
            set: fn (?string $value) => $value !== null ? preg_replace('/\D+/', '', $value) : null,
        );
    }

    public function branch(): BelongsTo
    {
        return $this->belongsTo(Branch::class);
    }

    public function household(): BelongsTo
    {
        return $this->belongsTo(Household::class);
    }

    public function headOfHousehold(): HasOne
    {
        return $this->hasOne(Household::class, 'head_member_id');
    }

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

    public function latestStatusChange(): HasOne
    {
        return $this->hasOne(MemberStatusHistory::class)->latestOfMany();
    }

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

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

    public function groups(): BelongsToMany
    {
        return $this->belongsToMany(Group::class, 'group_member')
            ->using(GroupMember::class)
            ->withPivot(['role_in_group', 'joined_at'])
            ->withTimestamps();
    }

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

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