<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Str;

/**
 * DigitalMachine Model
 *
 * Represents a digital health equipment marketing listing.
 * Supports multiple images, videos, price visibility control, and payment integration.
 *
 * @property int $id
 * @property int|null $user_id
 * @property string $name
 * @property string|null $short_name
 * @property string $slug
 * @property string|null $brand
 * @property string|null $model
 * @property string|null $category
 * @property string|null $vendor
 * @property string|null $manufacturers
 * @property array|null $images
 * @property array|null $video_clips
 * @property string|null $primary_image
 * @property string|null $description
 * @property array|null $specifications
 * @property array|null $functionalities
 * @property array|null $website_links
 * @property array|null $contact_info
 * @property float|null $price_ssp
 * @property float|null $price_usd
 * @property bool $price_publishable
 * @property bool $is_published
 * @property string $listing_status
 * @property string $payment_status
 *
 * Requirements: 3.1, 10.2, 10.3
 */
class DigitalMachine extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'user_id',
        'name',
        'short_name',
        'slug',
        'brand',
        'model',
        'category',
        'vendor',
        'manufacturers',
        'images',
        'video_clips',
        'primary_image',
        'description',
        'specifications',
        'functionalities',
        'website_links',
        'contact_info',
        'price_ssp',
        'price_usd',
        'price_publishable',
        'price_note',
        'is_published',
        'listing_status',
        'payment_status',
        'next_payment_date',
        'published_at',
        'expires_at',
        'approved_at',
        'approved_by',
        'rejection_reason',
        'view_count',
        'inquiry_count',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'images' => 'array',
        'video_clips' => 'array',
        'specifications' => 'array',
        'functionalities' => 'array',
        'website_links' => 'array',
        'contact_info' => 'array',
        'price_ssp' => 'decimal:2',
        'price_usd' => 'decimal:2',
        'price_publishable' => 'boolean',
        'is_published' => 'boolean',
        'next_payment_date' => 'datetime',
        'published_at' => 'datetime',
        'expires_at' => 'datetime',
        'approved_at' => 'datetime',
        'view_count' => 'integer',
        'inquiry_count' => 'integer',
    ];

    /**
     * The model's default values for attributes.
     *
     * @var array
     */
    protected $attributes = [
        'is_published' => false,
        'listing_status' => 'draft',
        'payment_status' => 'pending',
        'price_publishable' => true,
        'view_count' => 0,
        'inquiry_count' => 0,
    ];

    // =========================================================================
    // RELATIONSHIPS
    // =========================================================================

    /**
     * Get the user (Health Marketor) who owns this machine listing.
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    /**
     * Get the admin who approved this listing.
     */
    public function approver(): BelongsTo
    {
        return $this->belongsTo(User::class, 'approved_by');
    }

    /**
     * Get all payments for this machine.
     */
    public function payments(): HasMany
    {
        return $this->hasMany(MachinePayment::class, 'digital_machine_id');
    }

    /**
     * Get the latest successful payment.
     */
    public function latestPayment()
    {
        return $this->payments()
            ->where('status', 'successful')
            ->latest('paid_at')
            ->first();
    }

    // =========================================================================
    // SCOPES
    // =========================================================================

    /**
     * Scope a query to only include published machines.
     */
    public function scopePublished($query)
    {
        return $query->where('is_published', true)
            ->where('listing_status', 'published');
    }

    /**
     * Scope a query to filter by category.
     */
    public function scopeByCategory($query, string $category)
    {
        return $query->where('category', $category);
    }

    /**
     * Scope a query to filter by brand (exact match).
     */
    public function scopeByBrand($query, string $brand)
    {
        return $query->where('brand', $brand);
    }

    /**
     * Scope a query to only show machines with visible prices.
     */
    public function scopePriceVisible($query)
    {
        return $query->where('price_publishable', true);
    }

    /**
     * Scope a query to filter by listing status.
     */
    public function scopeByStatus($query, string $status)
    {
        return $query->where('listing_status', $status);
    }

    /**
     * Scope a query to search across multiple fields.
     * Functionalities are JSON so we use whereJsonContains for that column.
     */
    public function scopeSearch($query, string $term)
    {
        return $query->where(function ($q) use ($term) {
            $q->where('name', 'like', "%{$term}%")
                ->orWhere('short_name', 'like', "%{$term}%")
                ->orWhere('brand', 'like', "%{$term}%")
                ->orWhere('model', 'like', "%{$term}%")
                ->orWhere('category', 'like', "%{$term}%")
                ->orWhere('description', 'like', "%{$term}%")
                ->orWhereJsonContains('functionalities', $term);
        });
    }

    /**
     * Scope a query to only include active listings.
     */
    public function scopeActive($query)
    {
        return $query->whereIn('listing_status', ['published', 'pending_payment'])
            ->where(function ($q) {
                $q->whereNull('expires_at')
                    ->orWhere('expires_at', '>', now());
            });
    }

    /**
     * Scope a query to only include pending approval.
     */
    public function scopePendingApproval($query)
    {
        return $query->where('listing_status', 'pending_approval');
    }

    /**
     * Scope to order by latest.
     */
    public function scopeLatestPublished($query)
    {
        return $query->orderBy('published_at', 'desc');
    }

    // =========================================================================
    // ACCESSORS & MUTATORS
    // =========================================================================

    /**
     * Set the slug attribute.
     */
    public function setSlugAttribute($value)
    {
        $this->attributes['slug'] = $value ?? Str::slug($this->attributes['name'] ?? '');
    }

    /**
     * Get the primary image URL.
     */
    public function getPrimaryImageUrlAttribute(): string
    {
        if ($this->primary_image) {
            return asset('storage/' . $this->primary_image);
        }

        if (!empty($this->images) && is_array($this->images)) {
            return asset('storage/' . $this->images[0]);
        }

        return asset('images/default-machine.png');
    }

    /**
     * Get the formatted price.
     */
    public function getFormattedPriceAttribute(): string
    {
        if (!$this->price_publishable) {
            return 'Contact for Price';
        }

        $prices = [];

        if ($this->price_ssp) {
            $prices[] = 'SSP ' . number_format($this->price_ssp, 2);
        }

        if ($this->price_usd) {
            $prices[] = 'USD ' . number_format($this->price_usd, 2);
        }

        return !empty($prices) ? implode(' / ', $prices) : 'Price on Request';
    }

    /**
     * Get the functionalities as a formatted string.
     */
    public function getFunctionalitiesListAttribute(): string
    {
        if (empty($this->functionalities) || !is_array($this->functionalities)) {
            return '';
        }
        return implode(', ', $this->functionalities);
    }

    /**
     * Ensure contact_info is always an array.
     */
    public function getContactInfoAttribute($value): array
    {
        if (is_array($value)) {
            return $value;
        }
        
        if (is_string($value) && !empty($value)) {
            $decoded = json_decode($value, true);
            return is_array($decoded) ? $decoded : [];
        }
        
        return [];
    }

    /**
     * Ensure specifications is always a properly formatted array.
     */
    public function getSpecificationsAttribute($value): array
    {
        if (is_array($value)) {
            // Filter to ensure each item is an array with 'key' and 'value'
            return array_filter($value, function($spec) {
                return is_array($spec) && isset($spec['key']) && isset($spec['value']);
            });
        }
        
        if (is_string($value) && !empty($value)) {
            $decoded = json_decode($value, true);
            if (is_array($decoded)) {
                return array_filter($decoded, function($spec) {
                    return is_array($spec) && isset($spec['key']) && isset($spec['value']);
                });
            }
        }
        
        return [];
    }

    /**
     * Ensure functionalities is always an array of strings.
     */
    public function getFunctionalitiesAttribute($value): array
    {
        if (is_array($value)) {
            // Filter to ensure each item is a string or can be converted to string
            return array_filter($value, function($func) {
                return is_string($func) || is_numeric($func);
            });
        }
        
        if (is_string($value) && !empty($value)) {
            $decoded = json_decode($value, true);
            if (is_array($decoded)) {
                return array_filter($decoded, function($func) {
                    return is_string($func) || is_numeric($func);
                });
            }
        }
        
        return [];
    }

    /**
     * Ensure images is always an array.
     */
    public function getImagesAttribute($value): array
    {
        if (is_array($value)) {
            return $value;
        }
        
        if (is_string($value) && !empty($value)) {
            $decoded = json_decode($value, true);
            return is_array($decoded) ? $decoded : [];
        }
        
        return [];
    }

    /**
     * Ensure video_clips is always an array.
     */
    public function getVideoClipsAttribute($value): array
    {
        if (is_array($value)) {
            return $value;
        }
        
        if (is_string($value) && !empty($value)) {
            $decoded = json_decode($value, true);
            return is_array($decoded) ? $decoded : [];
        }
        
        return [];
    }

    /**
     * Get the status badge color.
     */
    public function getStatusBadgeColorAttribute(): string
    {
        return match ($this->listing_status) {
            'published' => 'green',
            'draft' => 'gray',
            'pending_payment' => 'yellow',
            'pending_approval' => 'blue',
            'paused' => 'orange',
            'expired' => 'red',
            'rejected' => 'red',
            'suspended' => 'red',
            default => 'gray',
        };
    }

    /**
     * Get all image URLs.
     */
    public function getImageUrlsAttribute(): array
    {
        if (empty($this->images) || !is_array($this->images)) {
            return [];
        }

        return array_map(function ($image) {
            return asset('storage/' . $image);
        }, $this->images);
    }

    /**
     * Get all video URLs.
     */
    public function getVideoUrlsAttribute(): array
    {
        if (empty($this->video_clips) || !is_array($this->video_clips)) {
            return [];
        }

        return array_map(function ($video) {
            // Check if it's a URL or a stored file
            if (filter_var($video, FILTER_VALIDATE_URL)) {
                return $video;
            }
            return asset('storage/' . $video);
        }, $this->video_clips);
    }

    // =========================================================================
    // HELPER METHODS
    // =========================================================================

    /**
     * Check if the machine is publicly viewable.
     */
    public function isPubliclyViewable(): bool
    {
        return $this->is_published && $this->listing_status === 'published';
    }

    /**
     * Check if the machine requires payment.
     */
    public function requiresPayment(): bool
    {
        if ($this->payment_status === 'exempt') {
            return false;
        }

        if ($this->payment_status === 'paid' && $this->next_payment_date) {
            return $this->next_payment_date->isPast();
        }

        return $this->payment_status !== 'paid';
    }

    /**
     * Check if the machine is expired.
     */
    public function isExpired(): bool
    {
        return $this->expires_at && $this->expires_at->isPast();
    }

    /**
     * Check if the machine is owned by a specific user.
     */
    public function isOwnedBy(User $user): bool
    {
        return $this->user_id === $user->id;
    }

    /**
     * Check if the machine needs admin approval.
     */
    public function needsApproval(): bool
    {
        return $this->listing_status === 'pending_approval';
    }

    /**
     * Generate a unique slug.
     */
    public static function generateUniqueSlug(string $name): string
    {
        $slug = Str::slug($name);
        $count = static::where('slug', 'like', "{$slug}%")->count();

        return $count > 0 ? "{$slug}-{$count}" : $slug;
    }

    /**
     * Approve the machine listing.
     */
    public function approve(int $approverId): void
    {
        $this->update([
            'listing_status' => 'published',
            'is_published' => true,
            'published_at' => now(),
            'approved_at' => now(),
            'approved_by' => $approverId,
            'rejection_reason' => null,
        ]);
    }

    /**
     * Reject the machine listing.
     */
    public function reject(int $approverId, string $reason): void
    {
        $this->update([
            'listing_status' => 'rejected',
            'is_published' => false,
            'approved_by' => $approverId,
            'rejection_reason' => $reason,
        ]);
    }

    /**
     * Increment view count.
     */
    public function incrementViewCount(): void
    {
        $this->increment('view_count');
    }

    /**
     * Increment inquiry count.
     */
    public function incrementInquiryCount(): void
    {
        $this->increment('inquiry_count');
    }

    // =========================================================================
    // STATIC METHODS
    // =========================================================================

    /**
     * Get listing status options.
     */
    public static function getListingStatuses(): array
    {
        return [
            'draft' => 'Draft',
            'pending_payment' => 'Pending Payment',
            'pending_approval' => 'Pending Approval',
            'published' => 'Published',
            'paused' => 'Paused',
            'expired' => 'Expired',
            'rejected' => 'Rejected',
            'suspended' => 'Suspended',
        ];
    }

    /**
     * Get common categories.
     */
    public static function getCategories(): array
    {
        return [
            'Diagnostic Equipment' => 'Diagnostic Equipment',
            'Surgical Equipment' => 'Surgical Equipment',
            'Laboratory Equipment' => 'Laboratory Equipment',
            'Monitoring Devices' => 'Monitoring Devices',
            'Imaging Equipment' => 'Imaging Equipment',
            'Therapeutic Equipment' => 'Therapeutic Equipment',
            'Life Support Equipment' => 'Life Support Equipment',
            'Medical Furniture' => 'Medical Furniture',
            'Sterilization Equipment' => 'Sterilization Equipment',
            'Dental Equipment' => 'Dental Equipment',
            'Ophthalmology Equipment' => 'Ophthalmology Equipment',
            'Rehabilitation Equipment' => 'Rehabilitation Equipment',
            'Other' => 'Other',
        ];
    }
}
