<?php

namespace App\Models\Accounts\Cronogramas;

use App\Enums\Accounts\Cronogramas\CronogramaActivityStatus;
use App\Enums\Accounts\Cronogramas\CronogramaResponsibleType;
use App\Models\Empresa;
use App\Models\Structure;
use App\Models\UnidadeOrganizacional;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
use DateTimeInterface;

/**
 * Atividade ou subatividade de um projeto de cronograma.
 */
class Activity extends Model {

    use SoftDeletes;

    protected $connection = 'mysql';

    public $incrementing = false;

    protected $keyType = 'string';

    protected $table = 'cr_activities';

    protected $fillable = [
        'id',
        'project_id',
        'code',
        'title',
        'description',
        'parent_id',
        'responsible_id',
        'responsible_type',
        'status',
        'completion_percent',
        'planned_start_date',
        'planned_end_date',
        'actual_start_date',
        'actual_end_date',
        'notes',
        'sort_order',
    ];

    protected $casts = [
        'completion_percent' => 'decimal:2',
        'planned_start_date' => 'date',
        'planned_end_date' => 'date',
        'actual_start_date' => 'date',
        'actual_end_date' => 'date',
    ];

    public static function boot(): void {
        parent::boot();

        Relation::morphMap([
            CronogramaResponsibleType::User->value      => User::class,
            CronogramaResponsibleType::Empresa->value   => Empresa::class,
            CronogramaResponsibleType::Structure->value => Structure::class,
            CronogramaResponsibleType::Unidade->value   => UnidadeOrganizacional::class,
        ]);

        static::creating(function (self $model): void {
            if (empty($model->id)) {
                $model->id = (string) Str::uuid();
            }
            if (empty($model->status)) {
                $model->status = CronogramaActivityStatus::NotStarted->value;
            }
        });
    }

    protected function serializeDate(DateTimeInterface $date): string {
        return $date->format('Y-m-d H:i:s');
    }

    public function project(): BelongsTo {
        return $this->belongsTo(Project::class, 'project_id', 'id');
    }

    public function parent(): BelongsTo {
        return $this->belongsTo(self::class, 'parent_id', 'id');
    }

    public function children(): HasMany {
        return $this->hasMany(self::class, 'parent_id', 'id')
            ->orderBy('sort_order');
    }

    public function responsible(): MorphTo {
        return $this->morphTo(__FUNCTION__, 'responsible_type', 'responsible_id');
    }

    public function responsibles(): HasMany {
        return $this->hasMany(ActivityResponsible::class, 'activity_id', 'id');
    }

    public function predecessors(): HasMany {
        return $this->hasMany(Dependency::class, 'activity_id', 'id');
    }

    public function successorDependencies(): HasMany {
        return $this->hasMany(Dependency::class, 'predecessor_id', 'id');
    }

    public function comments(): HasMany {
        return $this->hasMany(Comment::class, 'activity_id', 'id')
            ->orderByDesc('created_at');
    }

    public function attachments(): HasMany {
        return $this->hasMany(Attachment::class, 'activity_id', 'id')
            ->orderBy('created_at');
    }

    public function historyLogs(): HasMany {
        return $this->hasMany(HistoryLog::class, 'activity_id', 'id')
            ->orderByDesc('created_at');
    }

    public function isOverdue(): bool {
        if ($this->status === CronogramaActivityStatus::Completed->value
            || $this->status === CronogramaActivityStatus::Cancelled->value) {
            return false;
        }

        return $this->planned_end_date !== null
            && $this->planned_end_date->isPast();
    }

}
