<?php

namespace App\Services;

use App\Models\Attachment;
use App\Models\Departments;
use App\Models\Tickets;
use App\Models\User;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image;

class AttachmentService
{
    /**
     * Upload a file and create an attachment record.
     *
     * @param UploadedFile $file
     * @param int $ticketId
     * @param int $userId
     * @param int|null $responseId
     * @param bool $isFromEmail
     * @return Attachment
     * @throws \Exception
     */
    public static function upload(
        UploadedFile $file,
        int $ticketId,
        int $userId,
        ?int $responseId = null,
        bool $isFromEmail = false
    ): Attachment {
        // Get ticket and validate
        $ticket = Tickets::findOrFail($ticketId);
        $user = User::findOrFail($userId);

        // Get settings for this ticket's department
        $settings = static::getSettingsForDepartment($ticket->department_id);

        // Validate file
        static::validateFile($file, $settings, $ticket, $user);

        // Generate unique filename
        $hashedFilename = Str::random(40) . '.' . $file->getClientOriginalExtension();
        $storagePath = 'attachments/' . date('Y/m/d') . '/' . $hashedFilename;

        // Determine storage disk
        $storageDisk = $settings['storage_disk'] ?? 'local';

        // Store the file
        $file->storeAs(
            dirname($storagePath),
            basename($storagePath),
            $storageDisk
        );

        // Create attachment record
        $attachment = Attachment::create([
            'ticket_id' => $ticketId,
            'response_id' => $responseId,
            'user_id' => $userId,
            'filename' => $hashedFilename,
            'original_filename' => $file->getClientOriginalName(),
            'mime_type' => $file->getMimeType(),
            'size' => $file->getSize(),
            'storage_path' => $storagePath,
            'storage_disk' => $storageDisk,
            'is_from_email' => $isFromEmail,
            'virus_scanned' => false,
            'virus_detected' => false,
        ]);

        // Update user storage quota
        $user->increment('storage_used_bytes', $file->getSize());

        // Generate thumbnail if it's an image and thumbnails are enabled
        if ($settings['enable_thumbnails'] && $attachment->isImage()) {
            static::generateThumbnail($attachment);
        }

        // Queue virus scan if enabled
        if ($settings['require_virus_scan']) {
            // TODO: Dispatch virus scan job
            // dispatch(new ScanAttachmentForVirus($attachment));
        }

        return $attachment;
    }

    /**
     * Validate file against settings and quotas.
     *
     * @param UploadedFile $file
     * @param array $settings
     * @param Tickets $ticket
     * @param User $user
     * @throws \Exception
     */
    protected static function validateFile(
        UploadedFile $file,
        array $settings,
        Tickets $ticket,
        User $user
    ): void {
        // Check if attachments are enabled
        if (!$settings['enabled']) {
            throw new \Exception('File attachments are disabled for this department.');
        }

        // Check if user is an employee (exempt from most limits)
        $isEmployee = $user->hasAnyRole(['superadmin', 'admin', 'employee']);

        // Check file size (applies to everyone for security)
        $maxSizeMb = $settings['max_file_size_mb'];
        $maxSizeBytes = $maxSizeMb * 1024 * 1024;

        if ($file->getSize() > $maxSizeBytes) {
            throw new \Exception("File size exceeds the maximum allowed size of {$maxSizeMb}MB.");
        }

        // Check file type against allowed/blocked lists
        $extension = strtolower($file->getClientOriginalExtension());
        $mimeType = $file->getMimeType();

        if (!empty($settings['allowed_file_types'])) {
            // Whitelist mode - only allowed types permitted
            if (!in_array($extension, $settings['allowed_file_types']) &&
                !in_array($mimeType, $settings['allowed_file_types'])) {
                throw new \Exception('This file type is not allowed.');
            }
        }

        if (!empty($settings['blocked_file_types'])) {
            // Blacklist mode - blocked types rejected
            if (in_array($extension, $settings['blocked_file_types']) ||
                in_array($mimeType, $settings['blocked_file_types'])) {
                throw new \Exception('This file type is blocked.');
            }
        }

        // Check max files per ticket (skip for employees)
        if (!$isEmployee) {
            $existingCount = Attachment::where('ticket_id', $ticket->id)->count();
            if ($existingCount >= $settings['max_files_per_ticket']) {
                throw new \Exception("Maximum number of attachments ({$settings['max_files_per_ticket']}) reached for this ticket.");
            }
        }

        // Check user storage quota (skip for employees)
        if (!$isEmployee) {
            static::checkUserQuota($user, $file->getSize(), $settings);
        }
    }

    /**
     * Check if user has sufficient storage quota.
     *
     * @param User $user
     * @param int $fileSize
     * @param array $settings
     * @throws \Exception
     */
    protected static function checkUserQuota(User $user, int $fileSize, array $settings): void
    {
        // Determine quota: user-specific > department-specific > global
        $quotaMb = $user->storage_quota_mb
            ?? $settings['storage_quota_mb']
            ?? config('attachments.storage_quota_mb', 100);

        $quotaBytes = $quotaMb * 1024 * 1024;
        $currentUsage = $user->storage_used_bytes ?? 0;

        if (($currentUsage + $fileSize) > $quotaBytes) {
            $availableMb = round(($quotaBytes - $currentUsage) / 1024 / 1024, 2);
            throw new \Exception("Insufficient storage quota. You have {$availableMb}MB available.");
        }
    }

    /**
     * Get attachment settings for a specific department.
     * Falls back to global settings if department settings are null.
     *
     * @param int|null $departmentId
     * @return array
     */
    public static function getSettingsForDepartment(?int $departmentId): array
    {
        $globalSettings = [
            'enabled' => config('attachments.enabled', true),
            'max_file_size_mb' => config('attachments.max_file_size_mb', 10),
            'max_files_per_ticket' => config('attachments.max_files_per_ticket', 10),
            'allowed_file_types' => config('attachments.allowed_file_types', []),
            'blocked_file_types' => config('attachments.blocked_file_types', []),
            'storage_quota_mb' => config('attachments.storage_quota_mb', 100),
            'require_virus_scan' => config('attachments.require_virus_scan', false),
            'enable_thumbnails' => config('attachments.enable_thumbnails', true),
            'storage_disk' => config('attachments.storage_disk', 'local'),
        ];

        if (!$departmentId) {
            return $globalSettings;
        }

        $department = Departments::find($departmentId);

        if (!$department) {
            return $globalSettings;
        }

        // Merge department settings over global settings (department settings take precedence)
        return [
            'enabled' => $department->attachments_enabled ?? $globalSettings['enabled'],
            'max_file_size_mb' => $department->max_file_size_mb ?? $globalSettings['max_file_size_mb'],
            'max_files_per_ticket' => $department->max_files_per_ticket ?? $globalSettings['max_files_per_ticket'],
            'allowed_file_types' => !is_null($department->allowed_file_types) ? $department->allowed_file_types : $globalSettings['allowed_file_types'],
            'blocked_file_types' => !is_null($department->blocked_file_types) ? $department->blocked_file_types : $globalSettings['blocked_file_types'],
            'storage_quota_mb' => $department->storage_quota_mb ?? $globalSettings['storage_quota_mb'],
            'require_virus_scan' => $department->require_virus_scan ?? $globalSettings['require_virus_scan'],
            'enable_thumbnails' => $department->enable_thumbnails ?? $globalSettings['enable_thumbnails'],
            'storage_disk' => $globalSettings['storage_disk'],
        ];
    }

    /**
     * Generate thumbnail for image attachment.
     *
     * @param Attachment $attachment
     * @return void
     */
    protected static function generateThumbnail(Attachment $attachment): void
    {
        try {
            // Only generate thumbnails for images
            if (!$attachment->isImage()) {
                return;
            }

            $thumbnailWidth = config('attachments.thumbnail_width', 200);
            $thumbnailHeight = config('attachments.thumbnail_height', 200);

            // Get full path to original file
            $originalPath = Storage::disk($attachment->storage_disk)->path($attachment->storage_path);

            // Generate thumbnail path
            $thumbnailPath = str_replace(
                $attachment->filename,
                'thumb_' . $attachment->filename,
                $attachment->storage_path
            );

            // Create thumbnail using Intervention Image (if installed)
            if (class_exists(Image::class)) {
                $image = Image::make($originalPath);
                $image->fit($thumbnailWidth, $thumbnailHeight);

                $thumbnailFullPath = Storage::disk($attachment->storage_disk)->path($thumbnailPath);
                $image->save($thumbnailFullPath);

                // Update attachment record
                $attachment->update(['thumbnail_path' => $thumbnailPath]);
            }
        } catch (\Exception $e) {
            // Log error but don't fail - thumbnails are optional
            logger()->warning("Failed to generate thumbnail for attachment {$attachment->id}: " . $e->getMessage());
        }
    }

    /**
     * Delete an attachment and its physical file.
     *
     * @param Attachment $attachment
     * @return bool
     */
    public static function delete(Attachment $attachment): bool
    {
        try {
            // Eloquent model's deleted event will handle file deletion
            return $attachment->delete();
        } catch (\Exception $e) {
            logger()->error("Failed to delete attachment {$attachment->id}: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Get total storage used by all attachments.
     *
     * @return int Bytes
     */
    public static function getTotalStorageUsed(): int
    {
        return Attachment::sum('size');
    }

    /**
     * Get storage used by a specific user.
     *
     * @param int $userId
     * @return int Bytes
     */
    public static function getUserStorageUsed(int $userId): int
    {
        return Attachment::where('user_id', $userId)->sum('size');
    }

    /**
     * Get storage used by a specific ticket.
     *
     * @param int $ticketId
     * @return int Bytes
     */
    public static function getTicketStorageUsed(int $ticketId): int
    {
        return Attachment::where('ticket_id', $ticketId)->sum('size');
    }

    /**
     * Verify if user can download attachment (authorization check).
     *
     * @param Attachment $attachment
     * @param User $user
     * @return bool
     */
    public static function canDownload(Attachment $attachment, User $user): bool
    {
        // Superadmin, admin, and employees can download any attachment
        if ($user->hasAnyRole(['superadmin', 'admin', 'employee'])) {
            return true;
        }

        // Customers can only download attachments from their own tickets
        $ticket = $attachment->ticket;

        return $ticket && $ticket->user_id === $user->id;
    }

    /**
     * Format bytes to human-readable size.
     *
     * @param int $bytes
     * @param int $precision
     * @return string
     */
    public static function formatBytes(int $bytes, int $precision = 2): string
    {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];

        for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
            $bytes /= 1024;
        }

        return round($bytes, $precision) . ' ' . $units[$i];
    }
}
