<?php

namespace App\Models\Tenants;

use App\Traits\Auditable;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * 결제 모델
 *
 * @property int $id
 * @property int $subscription_id 구독 ID
 * @property float $amount 결제 금액
 * @property string $payment_method 결제 수단
 * @property string|null $transaction_id PG 거래 ID
 * @property Carbon|null $paid_at 결제일시
 * @property string $status 결제 상태
 * @property string|null $memo 메모
 *
 * @mixin IdeHelperPayment
 */
class Payment extends Model
{
    use Auditable, SoftDeletes;

    // =========================================================================
    // 상수 정의
    // =========================================================================

    /** 결제 상태 */
    public const STATUS_PENDING = 'pending';

    public const STATUS_COMPLETED = 'completed';

    public const STATUS_FAILED = 'failed';

    public const STATUS_CANCELLED = 'cancelled';

    public const STATUS_REFUNDED = 'refunded';

    public const STATUSES = [
        self::STATUS_PENDING,
        self::STATUS_COMPLETED,
        self::STATUS_FAILED,
        self::STATUS_CANCELLED,
        self::STATUS_REFUNDED,
    ];

    /** 상태 라벨 */
    public const STATUS_LABELS = [
        self::STATUS_PENDING => '대기',
        self::STATUS_COMPLETED => '완료',
        self::STATUS_FAILED => '실패',
        self::STATUS_CANCELLED => '취소',
        self::STATUS_REFUNDED => '환불',
    ];

    /** 결제 수단 */
    public const METHOD_CARD = 'card';

    public const METHOD_BANK = 'bank';

    public const METHOD_VIRTUAL = 'virtual';

    public const METHOD_CASH = 'cash';

    public const METHOD_FREE = 'free';

    public const PAYMENT_METHODS = [
        self::METHOD_CARD,
        self::METHOD_BANK,
        self::METHOD_VIRTUAL,
        self::METHOD_CASH,
        self::METHOD_FREE,
    ];

    /** 결제 수단 라벨 */
    public const METHOD_LABELS = [
        self::METHOD_CARD => '카드',
        self::METHOD_BANK => '계좌이체',
        self::METHOD_VIRTUAL => '가상계좌',
        self::METHOD_CASH => '현금',
        self::METHOD_FREE => '무료',
    ];

    // =========================================================================
    // 모델 설정
    // =========================================================================

    protected $fillable = [
        'subscription_id',
        'amount',
        'payment_method',
        'transaction_id',
        'paid_at',
        'status',
        'memo',
        'created_by',
        'updated_by',
        'deleted_by',
    ];

    protected $casts = [
        'amount' => 'float',
        'paid_at' => 'datetime',
    ];

    protected $attributes = [
        'status' => self::STATUS_PENDING,
        'payment_method' => self::METHOD_CARD,
    ];

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

    /**
     * 완료된 결제만
     */
    public function scopeCompleted(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_COMPLETED);
    }

    /**
     * 특정 상태
     */
    public function scopeOfStatus(Builder $query, string $status): Builder
    {
        return $query->where('status', $status);
    }

    /**
     * 특정 결제 수단
     */
    public function scopeOfMethod(Builder $query, string $method): Builder
    {
        return $query->where('payment_method', $method);
    }

    /**
     * 기간별 필터
     */
    public function scopeBetweenDates(Builder $query, ?string $startDate, ?string $endDate): Builder
    {
        if ($startDate) {
            $query->where('paid_at', '>=', $startDate);
        }
        if ($endDate) {
            $query->where('paid_at', '<=', $endDate.' 23:59:59');
        }

        return $query;
    }

    // =========================================================================
    // 관계
    // =========================================================================

    public function subscription(): BelongsTo
    {
        return $this->belongsTo(Subscription::class);
    }

    // =========================================================================
    // 접근자
    // =========================================================================

    /**
     * 상태 라벨
     */
    public function getStatusLabelAttribute(): string
    {
        return self::STATUS_LABELS[$this->status] ?? $this->status;
    }

    /**
     * 결제 수단 라벨
     */
    public function getPaymentMethodLabelAttribute(): string
    {
        return self::METHOD_LABELS[$this->payment_method] ?? $this->payment_method;
    }

    /**
     * 포맷된 금액
     */
    public function getFormattedAmountAttribute(): string
    {
        return number_format($this->amount).'원';
    }

    /**
     * 결제 완료 여부
     */
    public function getIsCompletedAttribute(): bool
    {
        return $this->status === self::STATUS_COMPLETED;
    }

    /**
     * 환불 가능 여부
     */
    public function getIsRefundableAttribute(): bool
    {
        return $this->status === self::STATUS_COMPLETED;
    }

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

    /**
     * 결제 완료 처리
     */
    public function complete(?string $transactionId = null): bool
    {
        if ($this->status !== self::STATUS_PENDING) {
            return false;
        }

        $this->status = self::STATUS_COMPLETED;
        $this->paid_at = now();

        if ($transactionId) {
            $this->transaction_id = $transactionId;
        }

        return $this->save();
    }

    /**
     * 결제 실패 처리
     */
    public function fail(?string $reason = null): bool
    {
        if ($this->status !== self::STATUS_PENDING) {
            return false;
        }

        $this->status = self::STATUS_FAILED;

        if ($reason) {
            $this->memo = $reason;
        }

        return $this->save();
    }

    /**
     * 결제 취소 처리
     */
    public function cancel(?string $reason = null): bool
    {
        if (! in_array($this->status, [self::STATUS_PENDING, self::STATUS_COMPLETED])) {
            return false;
        }

        $this->status = self::STATUS_CANCELLED;

        if ($reason) {
            $this->memo = $reason;
        }

        return $this->save();
    }

    /**
     * 환불 처리
     */
    public function refund(?string $reason = null): bool
    {
        if ($this->status !== self::STATUS_COMPLETED) {
            return false;
        }

        $this->status = self::STATUS_REFUNDED;

        if ($reason) {
            $this->memo = $reason;
        }

        return $this->save();
    }
}
