<?php

namespace App\Models;

use App\Traits\Uuid;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
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\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;

class Contract extends Model
{
    /** @use HasFactory<\Database\Factories\ContractFactory> */
    use HasFactory;

    use SoftDeletes;
    use Uuid;

    protected $fillable = [
        'reference',
        'start_date',
        'end_date',
        'client_case_id',
        'type_id',
        'active',
        'description',
        'meta',
    ];

    protected $hidden = [
        'id',
        'client_case_id',
        'type_id',
    ];

    protected function casts(): array
    {
        return [
            'meta' => 'array',
        ];
    }

    /**
     * Normalize start_date inputs to Y-m-d (or null) on assignment.
     */
    protected function startDate(): Attribute
    {
        return Attribute::make(
            set: function ($value) {
                if (is_null($value)) {
                    return null;
                }
                $str = is_string($value) ? $value : (string) $value;

                return \App\Services\DateNormalizer::toDate($str);
            }
        );
    }

    /**
     * Normalize end_date inputs to Y-m-d (or null) on assignment.
     */
    protected function endDate(): Attribute
    {
        return Attribute::make(
            set: function ($value) {
                if (is_null($value)) {
                    return null;
                }
                $str = is_string($value) ? $value : (string) $value;

                return \App\Services\DateNormalizer::toDate($str);
            }
        );
    }

    /**
     * Scope contracts to those in a specific segment with active pivot.
     */
    #[Scope]
    public function scopeForSegment(Builder $query, int $segmentId): Builder
    {
        return $query->whereExists(function ($q) use ($segmentId) {
            $q->from('contract_segment')
                ->whereColumn('contract_segment.contract_id', 'contracts.id')
                ->where('contract_segment.segment_id', $segmentId)
                ->where('contract_segment.active', true);
        });
    }

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

    public function clientCase(): BelongsTo
    {
        return $this->belongsTo(\App\Models\ClientCase::class)
            ->with(['person']);
    }

    public function segments(): BelongsToMany
    {
        return $this->belongsToMany(\App\Models\Segment::class)
            ->withPivot('active', 'created_at')
            ->wherePivot('active', true);
    }

    public function attachedSegments(): BelongsToMany
    {
        return $this->belongsToMany(\App\Models\Segment::class);
    }

    public function account(): HasOne
    {
        // Use latestOfMany to always surface newest account snapshot if multiple exist.
        return $this->hasOne(\App\Models\Account::class)
            ->latestOfMany()
            ->with('type');
    }

    public function objects(): HasMany
    {
        return $this->hasMany(\App\Models\CaseObject::class, 'contract_id');
    }

    public function documents(): MorphMany
    {
        return $this->morphMany(\App\Models\Document::class, 'documentable');
    }

    public function fieldJobs(): HasMany
    {
        return $this->hasMany(\App\Models\FieldJob::class);
    }

    public function lastFieldJobs(): HasOne {
        return $this->hasOne(\App\Models\FieldJob::class)->latestOfMany();
    }

    public function latestObject(): HasOne
    {
        return $this->hasOne(\App\Models\CaseObject::class)
            ->whereNull('deleted_at')
            ->latest();
    }

    protected static function booted(): void
    {
        static::created(function (Contract $contract): void {
            // Only apply configs when type and client case are set and no segments are already attached
            if (empty($contract->type_id) || empty($contract->client_case_id)) {
                return;
            }

            $existing = \DB::table('contract_segment')
                ->where('contract_id', $contract->id)
                ->count();
            if ($existing > 0) {
                // Respect pre-attached segments (e.g. custom import logic)
                return;
            }

            $configs = ContractConfig::query()
                ->where('contract_type_id', $contract->type_id)
                ->where('active', true)
                ->get(['segment_id', 'is_initial']);

            if ($configs->isEmpty()) {
                return;
            }

            foreach ($configs as $cfg) {
                // Ensure the segment is attached to the client case and active
                $attached = \DB::table('client_case_segment')
                    ->where('client_case_id', $contract->client_case_id)
                    ->where('segment_id', $cfg->segment_id)
                    ->first();
                if (! $attached) {
                    \DB::table('client_case_segment')->insert([
                        'client_case_id' => $contract->client_case_id,
                        'segment_id' => $cfg->segment_id,
                        'active' => true,
                        'created_at' => now(),
                        'updated_at' => now(),
                    ]);
                } elseif (! $attached->active) {
                    \DB::table('client_case_segment')
                        ->where('id', $attached->id)
                        ->update(['active' => true, 'updated_at' => now()]);
                }

                // Attach to contract; mark active if initial, otherwise inactive
                $pivot = \DB::table('contract_segment')
                    ->where('contract_id', $contract->id)
                    ->where('segment_id', $cfg->segment_id)
                    ->first();

                $activeFlag = $cfg->is_initial ? true : false;
                if ($pivot) {
                    \DB::table('contract_segment')
                        ->where('id', $pivot->id)
                        ->update(['active' => $activeFlag, 'updated_at' => now()]);
                } else {
                    \DB::table('contract_segment')->insert([
                        'contract_id' => $contract->id,
                        'segment_id' => $cfg->segment_id,
                        'active' => $activeFlag,
                        'created_at' => now(),
                        'updated_at' => now(),
                    ]);
                }
            }
        });
    }
}
