<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Storage;

class OrderItem extends Model
{
    use HasFactory;

    public const MINIMUM_ORDER_SUBTOTAL = 10000;

    const STATUS_PENDING = 'pending';
    const STATUS_PROSES_CETAK = 'proses_cetak';
    const STATUS_FINISHING = 'finishing';
    const STATUS_SELESAI = 'selesai';

    const PRODUCTION_STATUSES = [
        self::STATUS_PENDING => 'Pending',
        self::STATUS_PROSES_CETAK => 'Proses Cetak',
        self::STATUS_FINISHING => 'Finishing',
        self::STATUS_SELESAI => 'Selesai',
    ];

    protected $fillable = [
        'order_id',
        'product_id',
        'product_name',
        'material_id',
        'material_name',
        'file_name',
        'file_path',
        'preview_image_path',
        'size_description',
        'size_value',
        'size_unit',
        'qty',
        'sisi',
        'base_price',
        'calculated_price',
        'system_subtotal',
        'manual_price',
        'minimum_order_applied',
        'subtotal',
        'finishing_note',
        'production_status',
        'item_started_at',
        'item_finished_at',
        'is_cancelled',
        'cancelled_at',
        'cancelled_by',
        'cancel_reason',
    ];

    protected function casts(): array
    {
        return [
            'size_value' => 'decimal:4',
            'base_price' => 'decimal:2',
            'calculated_price' => 'decimal:2',
            'system_subtotal' => 'decimal:2',
            'manual_price' => 'decimal:2',
            'minimum_order_applied' => 'boolean',
            'subtotal' => 'decimal:2',
            'qty' => 'integer',
            'item_started_at' => 'datetime',
            'item_finished_at' => 'datetime',
            'is_cancelled' => 'boolean',
            'cancelled_at' => 'datetime',
            'sisi' => 'integer',
        ];
    }

    protected static function booted(): void
    {
        static::saving(function (self $item) {
            $item->calculatePrices();
        });

        static::saved(function (self $item) {
            $item->order?->recalculateTotal();
        });

        static::deleted(function (self $item) {
            $item->order?->recalculateTotal();
        });
    }

    protected function normalizedSizeUnit(): string
    {
        $unit = strtolower(trim((string) ($this->size_unit ?? 'lembar')));

        if ($unit === 'm²') {
            return 'm2';
        }

        if ($unit === 'pcs') {
            return 'lembar';
        }

        return $unit !== '' ? $unit : 'lembar';
    }

    public function getPriceMultiplier(): float
    {
        $unit = $this->normalizedSizeUnit();
        $sizeValue = (float) ($this->size_value ?? 0);

        if (in_array($unit, ['m2', 'meter'], true)) {
            return max($sizeValue, 0.0001);
        }

        return 1.0;
    }

    public function resolveStockUsageQty(): float
    {
        $unit = $this->normalizedSizeUnit();
        $qty = max((float) ($this->qty ?? 0), 0);

        if (in_array($unit, ['m2', 'meter'], true)) {
            return round($this->getPriceMultiplier() * $qty, 4);
        }

        return round($qty, 4);
    }

    protected function restoreMaterialStock(Material $material, float $qty, string $note): void
    {
        $qty = round(max($qty, 0), 4);

        if ($qty <= 0) {
            return;
        }

        if (method_exists($material, 'addStock')) {
            $material->addStock($qty, 0, $note, $this->order);
            return;
        }

        if (method_exists($material, 'increaseStock')) {
            $material->increaseStock($qty, $note, $this->order);
            return;
        }

        if (method_exists($material, 'returnStock')) {
            $material->returnStock($qty, $note, $this->order);
            return;
        }

        foreach (['stock', 'stock_qty', 'quantity', 'qty', 'current_stock', 'available_stock'] as $column) {
            if (Schema::hasColumn($material->getTable(), $column)) {
                $material->increment($column, $qty);
                return;
            }
        }

        throw new \RuntimeException('Method tambah stok tidak ditemukan pada model Material. Tambahkan addStock()/increaseStock() atau pastikan kolom stok tersedia.');
    }

    public function calculatePrices(): void
    {
        $qty = max((int) ($this->qty ?? 1), 1);
        $basePrice = max((float) ($this->base_price ?? 0), 0);
        $multiplier = $this->getPriceMultiplier();

        $this->calculated_price = round($basePrice * $multiplier, 2);

        $systemSubtotal = round($this->calculated_price * $qty, 2);
        $manualPrice = max((float) ($this->manual_price ?? 0), 0);

        $this->system_subtotal = $systemSubtotal;

        $isMeterBased = in_array($this->normalizedSizeUnit(), ['m2', 'meter'], true);

        if ($manualPrice > 0) {
            $this->subtotal = round($manualPrice, 2);
            $this->minimum_order_applied = false;
            return;
        }

        if ($isMeterBased && $systemSubtotal > 0 && $systemSubtotal < self::MINIMUM_ORDER_SUBTOTAL) {
            $this->subtotal = self::MINIMUM_ORDER_SUBTOTAL;
            $this->minimum_order_applied = true;
            return;
        }

        $this->subtotal = $systemSubtotal;
        $this->minimum_order_applied = false;
    }

    public function isManualPrice(): bool
    {
        return (float) ($this->manual_price ?? 0) > 0;
    }

    public function isMinimumOrderApplied(): bool
    {
        return (bool) ($this->minimum_order_applied ?? false);
    }

    // Status checks
    public function isPending(): bool
    {
        return $this->production_status === self::STATUS_PENDING;
    }

    public function isProsesCetak(): bool
    {
        return $this->production_status === self::STATUS_PROSES_CETAK;
    }

    public function isFinishing(): bool
    {
        return $this->production_status === self::STATUS_FINISHING;
    }

    public function isSelesai(): bool
    {
        return $this->production_status === self::STATUS_SELESAI;
    }

    public function getProductionStatusBadgeClass(): string
    {
        if ($this->is_cancelled) {
            return 'bg-error-50 text-error-700 dark:bg-error-500/15 dark:text-error-500';
        }

        return match ($this->production_status) {
            self::STATUS_SELESAI => 'bg-success-50 text-success-700 dark:bg-success-500/15 dark:text-success-500',
            self::STATUS_FINISHING => 'bg-blue-light-50 text-blue-light-700 dark:bg-blue-light-500/15 dark:text-blue-light-500',
            self::STATUS_PROSES_CETAK => 'bg-warning-50 text-warning-700 dark:bg-warning-500/15 dark:text-warning-500',
            default => 'bg-gray-100 text-gray-700 dark:bg-gray-500/15 dark:text-gray-400',
        };
    }

    public function getProductionStatusLabel(): string
    {
        if ($this->is_cancelled) {
            return 'Dibatalkan';
        }

        return self::PRODUCTION_STATUSES[$this->production_status] ?? $this->production_status;
    }

    public function isCancelled(): bool
    {
        return (bool) $this->is_cancelled;
    }

    public function canBeCancelled(): bool
    {
        return !$this->is_cancelled
            && $this->order
            && !$this->order->isCancelled()
            && !$this->order->isPickedUp();
    }

    public function hasPendingCancellationRequest(): bool
    {
        return OrderCancellationRequest::where('order_item_id', $this->id)
            ->where('status', OrderCancellationRequest::STATUS_PENDING)
            ->exists();
    }

    public function markAsCancelled(User $approver, string $reason): void
    {
        if ($this->material_id) {
            $material = Material::find($this->material_id);

            if ($material) {
                $this->restoreMaterialStock(
                    $material,
                    $this->resolveStockUsageQty(),
                    'Stok dikembalikan - Batal Item Order #' . ($this->order->invoice_number ?? '-')
                );
            }
        }

        $this->update([
            'is_cancelled' => true,
            'cancelled_at' => now(),
            'cancelled_by' => $approver->id,
            'cancel_reason' => $reason,
        ]);

        $this->order?->recalculateTotal();
        $this->order?->syncProductionStatus();
    }

    public function canStartItemProduction(): bool
    {
        $order = $this->order;

        return $order
            && ($order->isPaid() || $order->is_credit)
            && $this->isPending()
            && !$this->is_cancelled;
    }

    public function canMoveItemToFinishing(): bool
    {
        return $this->isProsesCetak() && !$this->is_cancelled;
    }

    public function canMarkItemComplete(): bool
    {
        return $this->isFinishing() && !$this->is_cancelled;
    }

    public function canRevertItemToPending(): bool
    {
        return $this->isProsesCetak();
    }

    public function canRevertItemToProsesCetak(): bool
    {
        return $this->isFinishing();
    }

    public function startProduction(): void
    {
        $this->update([
            'production_status' => self::STATUS_PROSES_CETAK,
            'item_started_at' => now(),
        ]);

        $this->order?->syncProductionStatus();
    }

    public function moveToFinishing(): void
    {
        $this->update([
            'production_status' => self::STATUS_FINISHING,
        ]);

        $this->order?->syncProductionStatus();
    }

    public function markAsComplete(): void
    {
        $this->update([
            'production_status' => self::STATUS_SELESAI,
            'item_finished_at' => now(),
        ]);

        $this->order?->syncProductionStatus();
    }

    public function revertToPending(): void
    {
        $this->update([
            'production_status' => self::STATUS_PENDING,
            'item_started_at' => null,
        ]);

        $this->order?->syncProductionStatus();
    }

    public function revertToProsesCetak(): void
    {
        $this->update([
            'production_status' => self::STATUS_PROSES_CETAK,
        ]);

        $this->order?->syncProductionStatus();
    }

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

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

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

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

    public function cancellationRequests()
    {
        return OrderCancellationRequest::where('order_item_id', $this->id);
    }

    public function getFormattedSubtotalAttribute(): string
    {
        return 'Rp ' . number_format((float) $this->subtotal, 0, ',', '.');
    }

    public function getSizeUnitLabelAttribute(): string
    {
        return match ($this->normalizedSizeUnit()) {
            'm2' => 'm²',
            'meter' => 'm',
            'lembar' => 'lembar',
            default => $this->size_unit ?? 'lembar',
        };
    }

    public function getFormattedCalculatedPriceAttribute(): string
    {
        return 'Rp ' . number_format((float) $this->calculated_price, 0, ',', '.');
    }

    public function getFormattedBasePriceAttribute(): string
    {
        return 'Rp ' . number_format((float) $this->base_price, 0, ',', '.');
    }

    public function getPreviewImageUrlAttribute(): ?string
    {
        return $this->preview_image_path ? Storage::url($this->preview_image_path) : null;
    }

    public static function fromProductAndMaterial(
        Product $product,
        ?Material $material,
        string $fileName,
        string $sizeDescription,
        float $sizeValue,
        string $sizeUnit,
        int $qty,
        float $basePrice,
        ?string $finishingNote = null
    ): self {
        return new self([
            'product_id' => $product->id,
            'product_name' => $product->name,
            'material_id' => $material?->id,
            'material_name' => $material?->name,
            'file_name' => $fileName,
            'size_description' => $sizeDescription,
            'size_value' => $sizeValue,
            'size_unit' => $sizeUnit,
            'qty' => $qty,
            'base_price' => $basePrice,
            'finishing_note' => $finishingNote,
        ]);
    }
}