<?php

namespace App\Models;

use App\Services\PublicSiteMediaService;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use App\Traits\HotelScopeTrait;
use Illuminate\Support\Facades\Auth;

class Room extends Model
{
    use HasFactory, \App\Traits\HotelScopeTrait;

    /** Statuses shown on the public guest site as bookable (vacant, not in maintenance). */
    public const PUBLIC_GUEST_LISTING_STATUSES = ['available', 'cleaning'];

    protected $fillable = [
        'hotel_id', // ADDED
        'room_number',
        'room_name',
        'floor',
        'room_type_id',
        'category',
        'price_per_night',
        'description',
        'capacity',
        'status',
        'housekeeping_status',
        'is_available',
        'amenities',
        'images',
        'last_cleaned_at',
        'last_checked_out_at',
        'housekeeping_notes'
    ];

    protected $casts = [
        'price_per_night' => 'float',
        'is_available' => 'boolean',
        'amenities' => 'array',
        'images' => 'array',
        'last_cleaned_at' => 'datetime',
        'last_checked_out_at' => 'datetime'
    ];

    /**
     * Boot the model.
     */
    protected static function boot()
    {
        parent::boot();
    }

    /**
     * Get the hotel that owns the room.
     */
    public function hotel(): BelongsTo
    {
        return $this->belongsTo(Hotel::class);
    }

    /**
     * Get the room type that owns the room.
     */
    public function roomType(): BelongsTo
    {
        return $this->belongsTo(RoomType::class);
    }

    /**
     * Get the reservations for the room.
     */
    public function reservations(): HasMany
    {
        return $this->hasMany(Reservation::class);
    }

    /**
     * Get the maintenance requests for the room.
     */
    public function maintenanceRequests(): HasMany
    {
        return $this->hasMany(Maintenance::class, 'room_id');
    }

    /**
     * Get housekeeping tasks for the room.
     */
    public function housekeepingTasks(): HasMany
    {
        return $this->hasMany(HousekeepingTask::class);
    }

    /**
     * Get the current housekeeping task for the room.
     */
    public function currentTask()
    {
        return $this->hasOne(HousekeepingTask::class, 'room_id')
                    ->whereIn('status', ['pending', 'in_progress'])
                    ->latest();
    }

    /**
     * Get the in-out logs for the room.
     */
    public function inOutLogs(): HasMany
    {
        return $this->hasMany(InOutLog::class);
    }

    /**
     * Get active maintenance requests (not completed/cancelled)
     */
    public function activeMaintenanceRequests(): HasMany
    {
        return $this->hasMany(Maintenance::class, 'room_id')
                    ->whereNotIn('status', ['completed', 'cancelled']);
    }

    /**
     * Get the active reservation for the room.
     */
    public function activeReservation()
    {
        return $this->hasOne(Reservation::class)
                    ->whereIn('status', ['confirmed', 'checked_in'])
                    ->latest();
    }

    // Scopes
    public function scopeAvailable($query)
    {
        return $query->where('status', 'available');
    }

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

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

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

    public function scopeUnderMaintenance($query)
    {
        return $query->where(function ($q) {
            $q->where('status', 'maintenance')
                ->orWhereHas('activeMaintenanceRequests');
        });
    }

    public function scopeByCategory($query, $category)
    {
        return $query->where('category', $category);
    }

    public function scopeByFloor($query, $floor)
    {
        return $query->where('floor', $floor);
    }

    // Accessors
    public function getIsAvailableAttribute(): bool
    {
        return $this->status === 'available';
    }

    public function getIsOccupiedAttribute(): bool
    {
        return $this->status === 'occupied';
    }

    public function getHasActiveMaintenanceAttribute(): bool
    {
        return $this->activeMaintenanceRequests()->exists();
    }

    /**
     * @return list<string> Paths relative to the public disk (e.g. room-images/photo.jpg)
     */
    protected function normalizedImagePaths(): array
    {
        $raw = $this->getRawOriginal('images') ?? $this->getAttributes()['images'] ?? null;
        $fromRaw = $this->decodeStoredImagesValue($raw);
        if ($fromRaw !== []) {
            return $fromRaw;
        }

        $cast = $this->getAttribute('images');
        if (is_array($cast)) {
            return array_values(array_filter($cast, fn ($v) => is_string($v) && $v !== ''));
        }

        return [];
    }

    /**
     * @return list<string>
     *
     * Handles three storage shapes safely:
     *   1. Native PHP array (already cast by Eloquent)
     *   2. JSON-encoded array — e.g. `["room-images\/file.webp"]`
     *   3. Double-encoded JSON — e.g. `"[\"room-images\\\/file.webp\"]"` (the
     *      legacy import format we see in production data).
     *
     * NOTE: We must NOT pre-normalize backslashes on the raw JSON string —
     * doing so corrupts escape sequences and prevents decoding. Backslash
     * normalization is applied per-path AFTER decoding instead.
     */
    protected function decodeStoredImagesValue(mixed $raw): array
    {
        if ($raw === null || $raw === '') {
            return [];
        }

        if (is_array($raw)) {
            return array_values(array_filter(array_map(
                fn ($v) => is_string($v) ? trim(str_replace('\\', '/', $v)) : null,
                $raw
            ), fn ($v) => is_string($v) && $v !== ''));
        }

        if (! is_string($raw)) {
            return [];
        }

        $s = trim($raw);

        for ($i = 0; $i < 4; $i++) {
            $decoded = json_decode($s, true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                break;
            }

            if (is_array($decoded)) {
                return array_values(array_filter(array_map(
                    fn ($v) => is_string($v) ? trim(str_replace('\\', '/', $v)) : null,
                    $decoded
                ), fn ($v) => is_string($v) && $v !== ''));
            }

            if (is_string($decoded)) {
                $s = trim($decoded);
                continue;
            }

            break;
        }

        $clean = trim(str_replace('\\', '/', $s));

        return $clean !== '' ? [$clean] : [];
    }

    /**
     * Build a browser URL for a stored path or full URL.
     *
     * Returns a root-relative URL (e.g. "/storage/room-images/foo.webp") so that
     * the browser resolves it against the current request origin. This avoids
     * APP_URL/dev-server-port mismatches (e.g. APP_URL=http://localhost vs the
     * actual host http://127.0.0.1:8000) that would otherwise produce broken
     * image links on the guest site.
     */
    public static function publicUrlForImagePath(string $path): string
    {
        $path = trim(str_replace('\\', '/', $path));
        if ($path === '') {
            return '';
        }

        if (preg_match('#^https?://#i', $path)) {
            return $path;
        }

        $path = ltrim($path, '/');
        if (str_starts_with($path, 'public/')) {
            $path = substr($path, strlen('public/'));
        }
        if (! str_starts_with($path, 'storage/')) {
            $path = 'storage/' . $path;
        }

        return '/' . $path;
    }

    public function getMainImageAttribute(): string
    {
        $paths = $this->normalizedImagePaths();

        if ($paths !== []) {
            $url = static::publicUrlForImagePath($paths[0]);

            return $url !== '' ? $url : 'https://via.placeholder.com/300x200/1a936f/ffffff?text=Room+' . $this->room_number;
        }

        return 'https://via.placeholder.com/300x200/1a936f/ffffff?text=Room+' . $this->room_number;
    }

    public function getImageUrlsAttribute(): array
    {
        return array_values(array_filter(array_map(function (string $image) {
            return static::publicUrlForImagePath($image);
        }, $this->normalizedImagePaths())));
    }

    public function getStatusBadgeClassAttribute(): string
    {
        return match($this->status) {
            'available' => 'success',
            'occupied' => 'primary',
            'cleaning' => 'warning',
            'maintenance' => 'danger',
            default => 'secondary'
        };
    }

    public function getHousekeepingStatusBadgeClassAttribute(): string
    {
        return match($this->housekeeping_status) {
            'clean' => 'success',
            'in_progress' => 'warning',
            'dirty' => 'danger',
            'checked_out' => 'danger',
            'maintenance' => 'info',
            'occupied' => 'primary',
            default => 'secondary'
        };
    }

    public function getHousekeepingStatusDisplayAttribute(): string
    {
        return match($this->housekeeping_status) {
            'clean' => 'Clean',
            'in_progress' => 'In Progress',
            'dirty' => 'Dirty',
            'checked_out' => 'Recently Checked Out',
            'maintenance' => 'Maintenance',
            'occupied' => 'Occupied',
            default => 'Unknown'
        };
    }

    public function getRecentlyCheckedOutAttribute(): bool
    {
        if (!$this->last_checked_out_at) {
            return false;
        }
        
        return $this->last_checked_out_at->gt(now()->subHours(4)) && 
               $this->housekeeping_status === 'dirty';
    }

    // Methods
    public function updateStatusBasedOnMaintenance(): void
    {
        if ($this->activeMaintenanceRequests()->exists()) {
            $this->update([
                'status' => 'maintenance',
                'housekeeping_status' => 'maintenance',
                'is_available' => false
            ]);
        } elseif ($this->status === 'maintenance') {
            $this->update([
                'status' => 'available',
                'housekeeping_status' => 'clean',
                'is_available' => true
            ]);
        }
    }

    public function updateStatus(string $housekeepingStatus): bool
    {
        $roomManagementStatus = $this->getRoomManagementStatus($housekeepingStatus);
        
        $updateData = [
            'housekeeping_status' => $housekeepingStatus,
            'status' => $roomManagementStatus,
        ];

        if ($housekeepingStatus === 'clean') {
            $updateData['last_cleaned_at'] = now();
            $updateData['is_available'] = true;
        } elseif (in_array($housekeepingStatus, ['dirty', 'in_progress'])) {
            $updateData['is_available'] = false;
        }
        
        return $this->update($updateData);
    }

    public function markAsCheckedOut(): bool
    {
        $updated = $this->update([
            'housekeeping_status' => 'dirty',
            'status' => 'cleaning',
            'last_checked_out_at' => now(),
            'is_available' => false
        ]);

        if ($updated && class_exists(\App\Models\HousekeepingTask::class)) {
            try {
                \App\Models\HousekeepingTask::create([
                    'hotel_id' => $this->hotel_id,
                    'room_id' => $this->id,
                    'task_type' => 'checkout',
                    'priority' => 'high',
                    'status' => 'pending',
                    'notes' => 'Checkout cleaning required',
                    'scheduled_date' => now()
                ]);
            } catch (\Exception $e) {
                \Log::warning('Failed to create housekeeping task: ' . $e->getMessage());
            }
        }

        return $updated;
    }

    public function markAsOccupied(): bool
    {
        return $this->update([
            'housekeeping_status' => 'occupied',
            'status' => 'occupied',
            'is_available' => false
        ]);
    }

    public function needsCleaning(): bool
    {
        return in_array($this->housekeeping_status, ['dirty', 'checked_out']);
    }

    public function isReadyForGuests(): bool
    {
        return $this->housekeeping_status === 'clean' && $this->status === 'available';
    }

    private function getRoomManagementStatus(string $housekeepingStatus): string
    {
        return match($housekeepingStatus) {
            'clean' => 'available',
            'dirty', 'in_progress', 'checked_out' => 'cleaning',
            'maintenance' => 'maintenance',
            'occupied' => 'occupied',
            default => 'cleaning'
        };
    }
}