<?php

declare(strict_types=1);

namespace App\Models\User\Report;

use App\Enums\Models\User\ApprovableStatus;
use App\Enums\Models\User\ReportActionType;
use App\Models\BaseModel;
use App\Models\User\Report;
use Database\Factories\User\Report\ReportStepFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Support\Carbon;

/**
 * @property ReportActionType|null $action
 * @property Model|null $actionable
 * @property string $actionable_type
 * @property int|null $actionable_id
 * @property array|null $fields
 * @property Carbon|null $finished_at
 * @property class-string<Model>|null $pivot
 * @property Report $report
 * @property ApprovableStatus $status
 * @property Model|null $target
 * @property string|null $target_type
 * @property int|null $target_id
 *
 * @method static ReportStepFactory factory(...$parameters)
 */
class ReportStep extends BaseModel
{
    use HasFactory;

    final public const string TABLE = 'report_step';

    final public const string ATTRIBUTE_ID = 'step_id';

    final public const string ATTRIBUTE_ACTION = 'action';
    final public const string ATTRIBUTE_ACTIONABLE_TYPE = 'actionable_type';
    final public const string ATTRIBUTE_ACTIONABLE_ID = 'actionable_id';

    final public const string ATTRIBUTE_TARGET_TYPE = 'target_type';
    final public const string ATTRIBUTE_TARGET_ID = 'target_id';

    final public const string ATTRIBUTE_PIVOT = 'pivot';

    final public const string ATTRIBUTE_REPORT = 'report_id';
    final public const string ATTRIBUTE_FIELDS = 'fields';
    final public const string ATTRIBUTE_FINISHED_AT = 'finished_at';
    final public const string ATTRIBUTE_STATUS = 'status';

    final public const string RELATION_ACTIONABLE = 'actionable';
    final public const string RELATION_TARGET = 'target';
    final public const string RELATION_REPORT = 'report';

    /**
     * Is auditing disabled?
     *
     * @var bool
     */
    public static $auditingDisabled = true;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        ReportStep::ATTRIBUTE_ACTION,
        ReportStep::ATTRIBUTE_ACTIONABLE_TYPE,
        ReportStep::ATTRIBUTE_ACTIONABLE_ID,
        ReportStep::ATTRIBUTE_FIELDS,
        ReportStep::ATTRIBUTE_PIVOT,
        ReportStep::ATTRIBUTE_REPORT,
        ReportStep::ATTRIBUTE_STATUS,
        ReportStep::ATTRIBUTE_TARGET_TYPE,
        ReportStep::ATTRIBUTE_TARGET_ID,
    ];

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = ReportStep::TABLE;

    /**
     * The primary key associated with the table.
     *
     * @var string
     */
    protected $primaryKey = ReportStep::ATTRIBUTE_ID;

    public function getName(): string
    {
        return strval($this->getKey());
    }

    public function getSubtitle(): string
    {
        return strval($this->getKey());
    }

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            ReportStep::ATTRIBUTE_ACTION => ReportActionType::class,
            ReportStep::ATTRIBUTE_FIELDS => 'array',
            ReportStep::ATTRIBUTE_FINISHED_AT => 'datetime',
            ReportStep::ATTRIBUTE_STATUS => ApprovableStatus::class,
        ];
    }

    /**
     * Format the fields to display in the admin panel
     * TODO: Double check if this action can be refactored.
     */
    public function formatFields(?array $fields = null): array
    {
        $newFields = [];
        $actionable = $this->actionable;

        if ($fields === null) {
            $fields = $this->fields;
        }

        foreach ($fields as $column => $value) {
            $casted = $actionable->hasCast($column)
                ? $actionable->castAttribute($column, $value)
                : $value;

            if (is_object($casted) && enum_exists($casted::class)) {
                $newFields[$column] = $casted->localize();
                continue;
            }

            $newFields[$column] = $casted;
        }

        return $newFields;
    }

    public function actionable(): MorphTo
    {
        return $this->morphTo();
    }

    public function target(): MorphTo
    {
        return $this->morphTo();
    }

    /**
     * @return BelongsTo<Report, $this>
     */
    public function report(): BelongsTo
    {
        return $this->belongsTo(Report::class, ReportStep::ATTRIBUTE_REPORT);
    }
}
