<?php

namespace App\Services;

use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\UploadedFile;
use Exception;

class LogoUploadServices
{
    /**
     * The storage disk to use for file operations
     */
    private $disk;

    /**
     * Allowed file extensions for logos
     */
    private const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp'];

    /**
     * Maximum file size in KB
     */
    private const MAX_FILE_SIZE = 2048;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->disk = Storage::disk('s3');
    }

    /**
     * Upload logo and manage client sub-folder
     *
     * @param UploadedFile $logoFile
     * @param mixed $azienda
     * @return string
     * @throws Exception
     */
    public function uploadLogo(UploadedFile $logoFile, $azienda): string
    {
        try {
            $currentClient = session('currentClient', 'default');

            if (!$currentClient) {
                throw new Exception('No current client found in session');
            }

            $this->validateLogoFile($logoFile);

            $this->ensureClientFolderExists($currentClient);

            $this->deleteExistingLogo($currentClient);

            $logoPath = $this->uploadNewLogo($logoFile, $currentClient);

            $azienda->logo = $logoPath;
            $azienda->save();

            Log::info("Logo uploaded successfully", [
                'client' => $currentClient,
                'path' => $logoPath,
                'azienda_id' => $azienda->id ?? null
            ]);

            return $logoPath;

        } catch (Exception $e) {
            Log::error('Error uploading logo', [
                'message' => $e->getMessage(),
                'client' => session('currentClient'),
                'azienda_id' => $azienda->id ?? null
            ]);
            throw $e;
        }
    }

    /**
     * Validate the uploaded logo file
     *
     * @param UploadedFile $logoFile
     * @throws Exception
     */
    private function validateLogoFile(UploadedFile $logoFile): void
    {
        // Check if file is valid
        if (!$logoFile->isValid()) {
            throw new Exception('Invalid file upload');
        }

        // Check file size
        if ($logoFile->getSize() > (self::MAX_FILE_SIZE * 1024)) {
            throw new Exception('File size exceeds maximum allowed size of ' . self::MAX_FILE_SIZE . 'KB');
        }

        // Check file extension
        $extension = strtolower($logoFile->getClientOriginalExtension());
        if (!in_array($extension, self::ALLOWED_EXTENSIONS)) {
            throw new Exception('File type not allowed. Allowed types: ' . implode(', ', self::ALLOWED_EXTENSIONS));
        }

        // Check mime type
        $mimeType = $logoFile->getMimeType();
        $allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
        if (!in_array($mimeType, $allowedMimeTypes)) {
            throw new Exception('Invalid file type');
        }
    }

    /**
     * Ensure client folder exists in the bucket
     *
     * @param string $clientName
     * @throws Exception
     */
    private function ensureClientFolderExists(string $clientName): void
    {
        try {
            $files = $this->disk->files($clientName);

            if (empty($files)) {
                $placeholderPath = $clientName . '/.gitkeep';
                $this->disk->put($placeholderPath, '');

                Log::info("Created client folder", ['client' => $clientName]);
            }

        } catch (Exception $e) {
            Log::error("Error creating client folder", [
                'client' => $clientName,
                'error' => $e->getMessage()
            ]);
            throw new Exception("Failed to create client folder: " . $e->getMessage());
        }
    }

    /**
     * Delete all existing logo files for the client
     *
     * @param string $clientName
     */
    private function deleteExistingLogo(string $clientName): void
    {
        try {
            foreach (self::ALLOWED_EXTENSIONS as $extension) {
                $logoPath = $clientName . '/logo.' . $extension;

                if ($this->disk->exists($logoPath)) {
                    $this->disk->delete($logoPath);
                    Log::info("Deleted existing logo", [
                        'client' => $clientName,
                        'file' => "logo.{$extension}"
                    ]);
                }
            }

        } catch (Exception $e) {
            Log::warning("Error deleting existing logo", [
                'client' => $clientName,
                'error' => $e->getMessage()
            ]);
        }
    }

    /**
     * Upload the new logo file
     *
     * @param UploadedFile $logoFile
     * @param string $clientName
     * @return string
     * @throws Exception
     */
    private function uploadNewLogo(UploadedFile $logoFile, string $clientName): string
    {
        try {
            $extension = strtolower($logoFile->getClientOriginalExtension());

            $fileName = 'logo.' . $extension;

            $logoPath = $this->disk->putFileAs(
                $clientName,
                $logoFile,
                $fileName,
                'private'
            );

            if (!$logoPath) {
                throw new Exception('Failed to upload file to storage');
            }

            Log::info("Logo file uploaded", [
                'client' => $clientName,
                'path' => $logoPath,
                'size' => $logoFile->getSize()
            ]);

            return $logoPath;

        } catch (Exception $e) {
            Log::error("Error uploading logo file", [
                'client' => $clientName,
                'error' => $e->getMessage()
            ]);
            throw new Exception("Failed to upload logo: " . $e->getMessage());
        }
    }

    /**
     * Get a temporary URL for the logo
     *
     * @param mixed $azienda
     * @param string $expiresIn
     * @return string|null
     */
    public function getLogoUrl($azienda, string $expiresIn = '+1 hour'): ?string
    {
        if (!$azienda->logo) {
            return null;
        }

        try {
            if (!$this->disk->exists($azienda->logo)) {
                Log::warning("Logo file not found", ['path' => $azienda->logo]);
                return null;
            }

            return $this->disk->temporaryUrl($azienda->logo, now()->add($expiresIn));

        } catch (Exception $e) {
            Log::error("Error generating logo URL", [
                'path' => $azienda->logo,
                'error' => $e->getMessage()
            ]);
            return null;
        }
    }

    /**
     * Check if logo file exists
     *
     * @param mixed $azienda
     * @return bool
     */
    public function logoExists($azienda): bool
    {
        if (!$azienda->logo) {
            return false;
        }

        return $this->disk->exists($azienda->logo);
    }

    /**
     * Delete logo file and update database
     *
     * @param mixed $azienda
     * @return bool
     */
    public function deleteLogo($azienda): bool
    {
        try {
            if ($azienda->logo && $this->disk->exists($azienda->logo)) {
                $this->disk->delete($azienda->logo);

                Log::info("Logo deleted", [
                    'path' => $azienda->logo,
                    'azienda_id' => $azienda->id ?? null
                ]);

                $azienda->logo = null;
                $azienda->save();

                return true;
            }

            return false;

        } catch (Exception $e) {
            Log::error("Error deleting logo", [
                'path' => $azienda->logo ?? 'unknown',
                'error' => $e->getMessage()
            ]);
            return false;
        }
    }

    /**
     * Get logo file information
     *
     * @param mixed $azienda
     * @return array|null
     */
    public function getLogoInfo($azienda): ?array
    {
        if (!$azienda->logo || !$this->disk->exists($azienda->logo)) {
            return null;
        }

        try {
            return [
                'path' => $azienda->logo,
                'size' => $this->disk->size($azienda->logo),
                'last_modified' => $this->disk->lastModified($azienda->logo),
                'url' => $this->getLogoUrl($azienda),
                'exists' => true
            ];

        } catch (Exception $e) {
            Log::error("Error getting logo info", [
                'path' => $azienda->logo,
                'error' => $e->getMessage()
            ]);
            return null;
        }
    }

    /**
     * List all logos for a client
     *
     * @param string|null $clientName
     * @return array
     */
    public function listClientLogos(?string $clientName = null): array
    {
        $clientName = $clientName ?? session('currentClient');

        if (!$clientName) {
            return [];
        }

        try {
            $files = $this->disk->files($clientName);

            return array_filter($files, function($file) {
                $filename = basename($file);
                return strpos($filename, 'logo.') === 0;
            });

        } catch (Exception $e) {
            Log::error("Error listing client logos", [
                'client' => $clientName,
                'error' => $e->getMessage()
            ]);
            return [];
        }
    }
}
