<?php

namespace App\Models\Client;

use App\Casts\DOB;
use App\Casts\EmailAddress;
use App\Casts\PhoneNumber;
use App\Interface\AddressOwner;
use App\Interface\CallableEntity;
use App\Interface\DocumentOwner;
use App\Interface\HistoryOwner;
use App\Interface\NoteOwner;
use App\Models\Claim\Claim;
use App\Models\ContractRequest;
use App\Models\LeadSource\LeadSource;
use App\Models\LeadSource\LeadSourceEndpoint;
use App\Models\OptIn;
use App\Models\Relation;
use App\Models\Tranche;
use App\Models\WipEntry;
use App\Trait\BelongsToSolicitor;
use App\Trait\HasAddresses;
use App\Trait\HasDocuments;
use App\Trait\HasHistory;
use App\Trait\HasMeta;
use App\Trait\HasName;
use App\Trait\HasNotes;
use App\Trait\HasSlug;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Carbon;

class Client extends Model implements AddressOwner, CallableEntity, DocumentOwner, HistoryOwner, NoteOwner
{
    use HasFactory;

    protected $table = 'clients';

    protected $fillable = [
        'solicitor_id',
        'slug',
        'reference',
        'title',
        'name_first',
        'name_middle',
        'name_last',
        'name_preferred',
        'date_of_birth',
        'phone_number',
        'landline_number',
        'email_address',
        'preferences',
        'lead_source_id',
        'lead_source_endpoint_id',
        'tranche_id',
        'contact_made',
        'first_successful_contact_at',
    ];

    protected $casts = [
        'date_of_birth' => DOB::class,
        'phone_number' => PhoneNumber::class,
        'email_address' => EmailAddress::class,
        'preferences' => 'array',
        'contact_made' => 'boolean',
        'first_successful_contact_at' => 'datetime',

    ];

    use BelongsToSolicitor, HasAddresses, HasDocuments, HasFactory, HasHistory, HasMeta, HasName, HasNotes, HasSlug, SoftDeletes;

    public function executor()
    {
        return $this->hasOne(Executor::class);
    }

    public function previousNames()
    {
        return $this->hasMany(PreviousName::class);
    }

    public function claim()
    {
        return $this->hasOne(Claim::class);
    }

    public function leadSource()
    {
        return $this->belongsTo(LeadSource::class);
    }

    public function endpoint()
    {
        return $this->belongsTo(LeadSourceEndpoint::class, 'lead_source_endpoint_id');
    }

    public function relations()
    {
        return $this->hasMany(Relation::class);
    }

    public function optins()
    {
        return $this->belongsToMany(OptIn::class, 'client_optins');
    }

    public function tranche()
    {
        return $this->belongsTo(Tranche::class);
    }

    public function confirmIdentity()
    {
        return $this->hasOne(ConfirmIdentity::class);
    }

    public function handlers()
    {
        return $this->hasMany(ClientHandler::class);
    }

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

    public function getDocumentStoragePath(): string
    {
        return 'clients/'.$this->slug.'/';
    }

    public function getPreferencesAttribute($value)
    {
        $defaults = [
            'home_address' => 0,
            'corp_address' => 0,
            'diff_format' => 0,
            'braille' => 0,
            'large_print' => 0,
            'audio' => 0,
            'other_lang' => 0,
        ];

        return array_merge($defaults, json_decode($value, true) ?: []);
    }

    public function getCallableNumber(): ?string
    {
        return (string) $this->phone_number;
    }

    public function getCallableLabel(): string
    {
        return $this->fullName;
    }

    public function getCallableType(): string
    {
        return 'client';
    }

    protected function displayId(): Attribute
    {
        return Attribute::make(
            get: fn () => 1 .str_pad($this->id, 5, '0', STR_PAD_LEFT),
        );
    }

    public function wipEntries()
    {
        return $this->hasMany(WipEntry::class);
    }

    /**
     * Record the first genuine two-way contact without ever moving the timestamp
     * forwards. This is called by answered client calls and matched inbound email.
     */
    public function recordSuccessfulContactAt(Carbon|string|null $at = null): void
    {
        $contactAt = $at instanceof Carbon ? $at : Carbon::parse($at ?? now());

        static::query()
            ->whereKey($this->getKey())
            ->where(function ($query) use ($contactAt) {
                $query->whereNull('first_successful_contact_at')
                    ->orWhere('first_successful_contact_at', '>', $contactAt);
            })
            ->update([
                'contact_made' => true,
                'first_successful_contact_at' => $contactAt,
                'updated_at' => now(),
            ]);

        $this->refresh();
    }
}
