<?php

namespace App\Models\Approvals;

use App\Models\User;
use App\Traits\BelongsToTenant;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;

class Approval extends Model
{
    use BelongsToTenant, SoftDeletes;

    protected $table = 'approvals';

    protected $casts = [
        'content' => 'array',
        'attachments' => 'array',
        'drafted_at' => 'datetime',
        'completed_at' => 'datetime',
        'drafter_read_at' => 'datetime',
        'current_step' => 'integer',
        'resubmit_count' => 'integer',
        'rejection_history' => 'array',
        'is_urgent' => 'boolean',
    ];

    protected $fillable = [
        'tenant_id',
        'document_number',
        'form_id',
        'line_id',
        'title',
        'content',
        'body',
        'status',
        'is_urgent',
        'drafter_id',
        'department_id',
        'drafted_at',
        'completed_at',
        'drafter_read_at',
        'current_step',
        'resubmit_count',
        'rejection_history',
        'attachments',
        'recall_reason',
        'parent_doc_id',
        'created_by',
        'updated_by',
        'deleted_by',
    ];

    protected $attributes = [
        'status' => 'draft',
        'current_step' => 0,
        'resubmit_count' => 0,
        'is_urgent' => false,
    ];

    // =========================================================================
    // 상태 상수
    // =========================================================================

    public const STATUS_DRAFT = 'draft';

    public const STATUS_PENDING = 'pending';

    public const STATUS_APPROVED = 'approved';

    public const STATUS_REJECTED = 'rejected';

    public const STATUS_CANCELLED = 'cancelled';

    public const STATUS_ON_HOLD = 'on_hold';

    public const STATUSES = [
        self::STATUS_DRAFT,
        self::STATUS_PENDING,
        self::STATUS_APPROVED,
        self::STATUS_REJECTED,
        self::STATUS_CANCELLED,
        self::STATUS_ON_HOLD,
    ];

    // =========================================================================
    // 관계 정의
    // =========================================================================

    public function form(): BelongsTo
    {
        return $this->belongsTo(ApprovalForm::class, 'form_id');
    }

    public function line(): BelongsTo
    {
        return $this->belongsTo(ApprovalLine::class, 'line_id');
    }

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

    public function steps(): HasMany
    {
        return $this->hasMany(ApprovalStep::class, 'approval_id')->orderBy('step_order');
    }

    public function approverSteps(): HasMany
    {
        return $this->hasMany(ApprovalStep::class, 'approval_id')
            ->whereIn('step_type', [ApprovalLine::STEP_TYPE_APPROVAL, ApprovalLine::STEP_TYPE_AGREEMENT])
            ->orderBy('step_order');
    }

    public function referenceSteps(): HasMany
    {
        return $this->hasMany(ApprovalStep::class, 'approval_id')
            ->where('step_type', ApprovalLine::STEP_TYPE_REFERENCE)
            ->orderBy('step_order');
    }

    public function parentDocument(): BelongsTo
    {
        return $this->belongsTo(self::class, 'parent_doc_id');
    }

    public function childDocuments(): HasMany
    {
        return $this->hasMany(self::class, 'parent_doc_id');
    }

    // =========================================================================
    // 스코프
    // =========================================================================

    public function scopeWithStatus($query, string $status)
    {
        return $query->where('status', $status);
    }

    public function scopeDraft($query)
    {
        return $query->where('status', self::STATUS_DRAFT);
    }

    public function scopePending($query)
    {
        return $query->where('status', self::STATUS_PENDING);
    }

    public function scopeApproved($query)
    {
        return $query->where('status', self::STATUS_APPROVED);
    }

    public function scopeRejected($query)
    {
        return $query->where('status', self::STATUS_REJECTED);
    }

    public function scopeOnHold($query)
    {
        return $query->where('status', self::STATUS_ON_HOLD);
    }

    public function scopeCompleted($query)
    {
        return $query->whereIn('status', [self::STATUS_APPROVED, self::STATUS_REJECTED, self::STATUS_CANCELLED]);
    }

    public function scopeByDrafter($query, int $userId)
    {
        return $query->where('drafter_id', $userId);
    }

    // =========================================================================
    // 헬퍼 메서드
    // =========================================================================

    public function isEditable(): bool
    {
        return in_array($this->status, [self::STATUS_DRAFT, self::STATUS_REJECTED]);
    }

    public function isSubmittable(): bool
    {
        return in_array($this->status, [self::STATUS_DRAFT, self::STATUS_REJECTED]);
    }

    public function isActionable(): bool
    {
        return $this->status === self::STATUS_PENDING;
    }

    public function isHoldable(): bool
    {
        return $this->status === self::STATUS_PENDING;
    }

    public function isHoldReleasable(): bool
    {
        return $this->status === self::STATUS_ON_HOLD;
    }

    public function isCancellable(): bool
    {
        return in_array($this->status, [self::STATUS_PENDING, self::STATUS_ON_HOLD]);
    }

    public function isCopyable(): bool
    {
        return in_array($this->status, [self::STATUS_APPROVED, self::STATUS_REJECTED, self::STATUS_CANCELLED]);
    }

    public function isDeletable(): bool
    {
        return $this->status === self::STATUS_DRAFT;
    }

    public function isDeletableBy(?User $user = null): bool
    {
        if (! $user) {
            return $this->isDeletable();
        }

        if ($user->isAdmin()) {
            return true;
        }

        return $this->status === self::STATUS_DRAFT
            && $this->drafter_id === $user->id;
    }

    public function getStatusLabelAttribute(): string
    {
        return match ($this->status) {
            self::STATUS_DRAFT => '임시저장',
            self::STATUS_PENDING => '진행',
            self::STATUS_APPROVED => '완료',
            self::STATUS_REJECTED => '반려',
            self::STATUS_CANCELLED => '회수',
            self::STATUS_ON_HOLD => '보류',
            default => $this->status,
        };
    }

    public function getStatusColorAttribute(): string
    {
        return match ($this->status) {
            self::STATUS_DRAFT => 'gray',
            self::STATUS_PENDING => 'blue',
            self::STATUS_APPROVED => 'green',
            self::STATUS_REJECTED => 'red',
            self::STATUS_CANCELLED => 'yellow',
            self::STATUS_ON_HOLD => 'amber',
            default => 'gray',
        };
    }

    public function getCurrentApproverStep(): ?ApprovalStep
    {
        return $this->steps()
            ->whereIn('step_type', [ApprovalLine::STEP_TYPE_APPROVAL, ApprovalLine::STEP_TYPE_AGREEMENT])
            ->where('status', ApprovalStep::STATUS_PENDING)
            ->orderBy('step_order')
            ->first();
    }

    public function isCurrentApprover(int $userId): bool
    {
        $currentStep = $this->getCurrentApproverStep();

        return $currentStep && $currentStep->approver_id === $userId;
    }

    public function isReferee(int $userId): bool
    {
        return $this->referenceSteps()
            ->where('approver_id', $userId)
            ->exists();
    }

    public function getProgressAttribute(): array
    {
        $totalSteps = $this->approverSteps()->count();
        $completedSteps = $this->approverSteps()
            ->whereIn('status', [ApprovalStep::STATUS_APPROVED, ApprovalStep::STATUS_REJECTED])
            ->count();

        return [
            'total' => $totalSteps,
            'completed' => $completedSteps,
            'percentage' => $totalSteps > 0 ? round(($completedSteps / $totalSteps) * 100) : 0,
        ];
    }
}
