<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Auth;
use Monzer\FilamentWorkflows\Traits\TrackWorkflowModelEvents;
use Spatie\Activitylog\LogOptions;
use Spatie\Activitylog\Models\Activity;
use Spatie\Activitylog\Traits\LogsActivity;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;

class ExchangeRequest extends Model implements HasMedia
{
    use HasFactory, InteractsWithMedia, LogsActivity, TrackWorkflowModelEvents, \Znck\Eloquent\Traits\BelongsToThrough;

    const STATUS_DRAFT = 'draft';

    const STATUS_CREATED = 'created';

    const STATUS_FINANCIAL_APPROVED = 'financial_approved';

    const STATUS_SHARED_SERVICES_APPROVED = 'shared_services_approved';

    const STATUS_CEO_APPROVED = 'ceo_approved';

    const STATUS_TRANSACTION_APPROVED = 'transaction_approved';

    const STATUS_APPROVED = 'approved';

    const STATUS_REJECTED = 'rejected';

    protected $fillable = [
        'status',
        'completion_report_id',
        'created_by',
        'transaction_date',
    ];

    protected $casts = [
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
        'transaction_date' => 'date',
    ];

    protected $attributes = [
        'status' => self::STATUS_DRAFT,
    ];

    protected static function boot()
    {
        parent::boot();

        static::creating(function ($model) {
            if (Auth::check()) {
                $model->created_by = Auth::id();
            }
        });
    }

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

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

    public function association(): \Znck\Eloquent\Relations\BelongsToThrough
    {
        return $this->belongsToThrough(Association::class, [Order::class, ProjectCharter::class, ProjectCharterDeliverable::class, CompletionReport::class]);
    }

    public function order(): \Znck\Eloquent\Relations\BelongsToThrough
    {
        return $this->belongsToThrough(Order::class, [ProjectCharter::class, ProjectCharterDeliverable::class, CompletionReport::class]);
    }

    public function projectCharterDeliverable(): \Znck\Eloquent\Relations\BelongsToThrough
    {
        return $this->belongsToThrough(ProjectCharterDeliverable::class, [CompletionReport::class]);
    }

    // related project charter
    public function projectCharter(): \Znck\Eloquent\Relations\BelongsToThrough
    {
        return $this->belongsToThrough(ProjectCharter::class, [ProjectCharterDeliverable::class, CompletionReport::class]);
    }

    #[Scope]
    protected function ownScope(Builder $builder): void
    {
        $user = Auth::user();
        if (! $user) {
            $builder->whereRaw('1 = 0');
        } elseif (! $user->can('view_all_exchange::request')) {
            $builder->where('created_by', $user->id);
        }
    }

    /**
     * Get available statuses
     */
    public static function getStatuses(): array
    {
        return [
            self::STATUS_DRAFT => __('exchange_request.status_draft'),
            self::STATUS_CREATED => __('exchange_request.status_created'),
            self::STATUS_FINANCIAL_APPROVED => __('exchange_request.status_financial_approved'),
            self::STATUS_SHARED_SERVICES_APPROVED => __('exchange_request.status_shared_services_approved'),
            self::STATUS_CEO_APPROVED => __('exchange_request.status_ceo_approved'),
            self::STATUS_TRANSACTION_APPROVED => __('exchange_request.status_transaction_approved'),
            self::STATUS_APPROVED => __('exchange_request.status_approved'),
            self::STATUS_REJECTED => __('exchange_request.status_rejected'),
        ];
    }

    /**
     * Get the next status for approval workflow
     */
    public function getNextStatus(): ?string
    {
        $statusFlow = [
            self::STATUS_DRAFT => self::STATUS_CREATED,
            self::STATUS_CREATED => self::STATUS_FINANCIAL_APPROVED,
            self::STATUS_FINANCIAL_APPROVED => self::STATUS_SHARED_SERVICES_APPROVED,
            self::STATUS_SHARED_SERVICES_APPROVED => self::STATUS_CEO_APPROVED,
            self::STATUS_CEO_APPROVED => self::STATUS_TRANSACTION_APPROVED,
            self::STATUS_TRANSACTION_APPROVED => self::STATUS_APPROVED,
            self::STATUS_APPROVED => null,
            self::STATUS_REJECTED => null,
        ];

        return $statusFlow[$this->status] ?? null;
    }

    /**
     * Get the previous status for sending back workflow
     */
    public function getPreviousStatus(): ?string
    {
        $statusFlow = [
            self::STATUS_DRAFT => null,
            self::STATUS_CREATED => null,
            self::STATUS_FINANCIAL_APPROVED => self::STATUS_CREATED,
            self::STATUS_SHARED_SERVICES_APPROVED => self::STATUS_FINANCIAL_APPROVED,
            self::STATUS_CEO_APPROVED => self::STATUS_SHARED_SERVICES_APPROVED,
            self::STATUS_TRANSACTION_APPROVED => self::STATUS_CEO_APPROVED,
            self::STATUS_APPROVED => null,
            self::STATUS_REJECTED => null,
        ];

        return $statusFlow[$this->status] ?? null;
    }

    /**
     * Check if exchange request can be sent back to previous stage
     */
    public function canSendBackToPreviousStage(): bool
    {
        // Check if we have a previous status
        return $this->getPreviousStatus() !== null;
    }

    /**
     * Send exchange request back to previous stage
     */
    public function sendBackToPreviousStage(string $notes = ''): bool
    {
        $previousStatus = $this->getPreviousStatus();
        if (! $previousStatus) {
            return false;
        }

        $oldStatus = $this->status;

        // Update status
        $this->status = $previousStatus;
        $result = $this->save();

        if ($result) {
            // Log the send back activity
            activity()
                ->performedOn($this)
                ->causedBy(Auth::user())
                ->event('send_back')
                ->withProperties([
                    'old_status' => $oldStatus,
                    'new_status' => $previousStatus,
                    'notes' => $notes,
                    'action' => 'sent_back',
                ])
                ->log("Exchange request sent back from {$oldStatus} to {$previousStatus}");
        }

        return $result;
    }

    /**
     * Check if exchange request can be approved to next stage
     */
    public function canApproveToNextStage(): bool
    {
        return $this->getNextStatus() !== null && $this->status !== self::STATUS_REJECTED;
    }

    /**
     * Check if exchange request can be rejected
     */
    public function canReject(): bool
    {
        return ! in_array($this->status, [self::STATUS_APPROVED, self::STATUS_REJECTED]);
    }

    /**
     * Approve exchange request to next stage
     */
    public function approveToNextStage(string $notes = ''): bool
    {
        $nextStatus = $this->getNextStatus();

        if (! $nextStatus || ! $this->canApproveToNextStage()) {
            return false;
        }

        $oldStatus = $this->status;
        $this->status = $nextStatus;
        $result = $this->save();

        if ($result) {
            activity()
                ->performedOn($this)
                ->causedBy(Auth::user())
                ->event('approved')
                ->withProperties([
                    'old_status' => $oldStatus,
                    'new_status' => $nextStatus,
                    'action' => 'approved',
                    'notes' => $notes,
                ])
                ->log('Exchange request approved to next stage');
        }

        return $result;
    }

    /**
     * Reject exchange request
     */
    public function reject(string $notes = ''): bool
    {
        if (! $this->canReject()) {
            return false;
        }

        $oldStatus = $this->status;
        $this->status = self::STATUS_REJECTED;
        $result = $this->save();

        if ($result) {
            activity()
                ->performedOn($this)
                ->causedBy(Auth::user())
                ->event('rejected')
                ->withProperties([
                    'old_status' => $oldStatus,
                    'new_status' => $this->status,
                    'action' => 'rejected',
                    'notes' => $notes,
                ])
                ->log('Exchange request rejected');
        }

        return $result;
    }

    /**
     * Register media collections
     */
    public function registerMediaCollections(): void
    {
        $this->addMediaCollection('transaction_file')
            ->singleFile()
            ->acceptsMimeTypes([
                'application/pdf',
                'image/jpeg',
                'image/png',
                'image/jpg',
            ]);
    }

    /**
     * Get the transaction file
     */
    public function getTransactionFile(): ?Media
    {
        return $this->getFirstMedia('transaction_file');
    }

    /**
     * Get the transaction file URL
     */
    public function getTransactionFileUrlAttribute(): ?string
    {
        return $this->getFirstMedia('transaction_file')?->getTemporaryUrl(Carbon::now()->addMinutes(5));
    }

    /**
     * Check if a status has been completed
     * A status is completed if:
     * 1. It has been approved
     * 2. The current status is BEYOND this status in the workflow (not equal)
     * 3. There's no send_back after the approval that invalidates it
     */
    public function isStatusCompleted(string $status): bool
    {
        // Current status is never "completed" - it's always "pending"
        if ($this->status === $status) {
            return false;
        }

        // Define the status order
        $statusOrder = [
            self::STATUS_DRAFT => 0,
            self::STATUS_CREATED => 1,
            self::STATUS_FINANCIAL_APPROVED => 2,
            self::STATUS_SHARED_SERVICES_APPROVED => 3,
            self::STATUS_CEO_APPROVED => 4,
            self::STATUS_TRANSACTION_APPROVED => 5,
            self::STATUS_APPROVED => 6,
            self::STATUS_REJECTED => 7,
        ];

        // Get the order of the status being checked and current status
        $checkingStatusOrder = $statusOrder[$status] ?? -1;
        $currentStatusOrder = $statusOrder[$this->status] ?? -1;

        // Current status must be BEYOND (not equal to) the status being checked
        if ($currentStatusOrder <= $checkingStatusOrder) {
            return false;
        }

        // Check if there's an approval activity for this status
        $activity = $this->getActivityForStatus($status);

        if (! $activity) {
            return false;
        }

        // Check if there's a send_back activity after the approval
        // that moved the request to a status before the one being checked
        $sendBackAfterApproval = $this->activities()
            ->where('event', 'send_back')
            ->where('created_at', '>', $activity->created_at)
            ->where('properties->new_status', '<>', $status) // Exclude send_back to the same status
            ->get()
            ->filter(function ($sendBackActivity) use ($statusOrder, $checkingStatusOrder) {
                $newStatus = $sendBackActivity->properties['new_status'] ?? null;
                if (! $newStatus) {
                    return false;
                }
                $newStatusOrder = $statusOrder[$newStatus] ?? -1;

                // If sent back to a status before the one we're checking, this invalidates the completion
                return $newStatusOrder < $checkingStatusOrder;
            })
            ->first();

        // If there's a send_back after the approval that moved to an earlier stage, not completed
        return $sendBackAfterApproval === null;
    }

    /**
     * Get activity for a specific status
     */
    public function getActivityForStatus(string $status): ?Activity
    {
        return $this->activities()
            ->where('event', 'approved')
            ->where('properties->old_status', $status)
            ->latest()
            ->first();
    }

    /**
     * Configure activity logging
     */
    public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults()
            ->logOnly(['status', 'transaction_date'])
            ->logOnlyDirty()
            ->dontSubmitEmptyLogs();
    }
}
