<?php

namespace App\Services;

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

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

    /**
     * Allowed file extensions for different file types
     */
    private const ALLOWED_IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif'];
    private const ALLOWED_DOCUMENT_EXTENSIONS = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx'];

    /**
     * Maximum file sizes in KB
     */
    private const MAX_IMAGE_SIZE = 2048; // 2MB
    private const MAX_DOCUMENT_SIZE = 10240; // 10MB

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

    /**
     * Upload member profile image
     *
     * @param UploadedFile $imageFile
     * @param int $memberId
     * @return string
     * @throws Exception
     */
    public function uploadProfileImage(UploadedFile $imageFile, int $memberId): string
    {
        try {
            $currentClient = session('currentClient', 'default');

            // Validate file
            $this->validateFile($imageFile, 'image');

            // Delete existing profile image
            $this->deleteExistingProfileImage($memberId);

            // Create new filename
            $extension = strtolower($imageFile->getClientOriginalExtension());
            $filename = 'profile_' . $memberId . '_' . time() . '.' . $extension;

            // Upload to S3
            $s3Path = $currentClient . '/members/' . $memberId . '/images/' . $filename;

            $uploaded = $this->disk->putFileAs(
                $currentClient . '/members/' . $memberId . '/images',
                $imageFile,
                $filename,
                'private'
            );

            if (!$uploaded) {
                throw new Exception('Failed to upload profile image to S3');
            }

            Log::info("Profile image uploaded", [
                'member_id' => $memberId,
                'path' => $s3Path,
                'size' => $imageFile->getSize()
            ]);

            return $s3Path;

        } catch (Exception $e) {
            Log::error("Error uploading profile image", [
                'member_id' => $memberId,
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }

    /**
     * Upload single document file
     *
     * @param UploadedFile $documentFile
     * @param int $memberId
     * @param string $documentType ('self', 'father', 'mother')
     * @return string
     * @throws Exception
     */
    public function uploadDocument(UploadedFile $documentFile, int $memberId, string $documentType = 'self'): string
    {
        try {
            $currentClient = session('currentClient', 'default');

            // Validate file
            $this->validateFile($documentFile, 'document');

            // Create filename
            $originalName = pathinfo($documentFile->getClientOriginalName(), PATHINFO_FILENAME);
            $extension = strtolower($documentFile->getClientOriginalExtension());
            $filename = $documentType . '_' . $originalName . '_' . time() . '.' . $extension;

            // Upload to S3
            $s3Path = $currentClient . '/members/' . $memberId . '/documents/' . $filename;

            $uploaded = $this->disk->putFileAs(
                $currentClient . '/members/' . $memberId . '/documents',
                $documentFile,
                $filename,
                'private'
            );

            if (!$uploaded) {
                throw new Exception('Failed to upload document to S3: ' . $originalName);
            }

            Log::info("Document uploaded", [
                'member_id' => $memberId,
                'type' => $documentType,
                'path' => $s3Path,
                'original_name' => $documentFile->getClientOriginalName()
            ]);

            return $s3Path;

        } catch (Exception $e) {
            Log::error("Error uploading document", [
                'member_id' => $memberId,
                'type' => $documentType,
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }

    /**
     * Upload certificate file
     *
     * @param UploadedFile $certificateFile
     * @param int $memberId
     * @return string
     * @throws Exception
     */
    public function uploadCertificate(UploadedFile $certificateFile, int $memberId): string
    {
        try {
            $currentClient = session('currentClient', 'default');

            // Validate file
            $this->validateFile($certificateFile, 'document');

            // Create filename
            $extension = strtolower($certificateFile->getClientOriginalExtension());
            $filename = 'certificate_' . $memberId . '_' . time() . '.' . $extension;

            // Upload to S3
            $s3Path = $currentClient . '/members/' . $memberId . '/certificates/' . $filename;

            $uploaded = $this->disk->putFileAs(
                $currentClient . '/members/' . $memberId . '/certificates',
                $certificateFile,
                $filename,
                'private'
            );

            if (!$uploaded) {
                throw new Exception('Failed to upload certificate to S3');
            }

            Log::info("Certificate uploaded", [
                'member_id' => $memberId,
                'path' => $s3Path,
                'size' => $certificateFile->getSize()
            ]);

            return $s3Path;

        } catch (Exception $e) {
            Log::error("Error uploading certificate", [
                'member_id' => $memberId,
                'error' => $e->getMessage()
            ]);
            throw $e;
        }
    }

    /**
     * Get file URL for display
     *
     * @param string $filePath
     * @param string $expiresIn
     * @return string|null
     */
    public function getFileUrl(string $filePath, string $expiresIn = '+1 hour'): ?string
    {
        if (!$filePath) {
            return null;
        }

        // Handle legacy local paths - return asset URL
        if (!$this->isS3Path($filePath)) {
            return asset('storage/' . $filePath);
        }

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

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

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

    /**
     * Delete file from S3
     *
     * @param string $filePath
     * @return bool
     */
    public function deleteFile(string $filePath): bool
    {
        // Don't try to delete local files
        if (!$this->isS3Path($filePath)) {
            return false;
        }

        try {
            if ($this->disk->exists($filePath)) {
                $this->disk->delete($filePath);
                Log::info("File deleted", ['path' => $filePath]);
                return true;
            }
            return false;

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

    /**
     * Check if path is S3 path
     */
    private function isS3Path(string $path): bool
    {
        return strpos($path, '/members/') !== false ||
               strpos($path, session('currentClient', 'default')) === 0;
    }

    /**
     * Delete existing profile image for member
     *
     * @param int $memberId
     */
    private function deleteExistingProfileImage(int $memberId): void
    {
        try {
            $currentClient = session('currentClient', 'default');
            $imagePath = $currentClient . '/members/' . $memberId . '/images/';

            // List all files in the member's image directory
            $files = $this->disk->files($imagePath);

            foreach ($files as $file) {
                if (strpos(basename($file), 'profile_' . $memberId) === 0) {
                    $this->disk->delete($file);
                    Log::info("Deleted existing profile image", [
                        'member_id' => $memberId,
                        'file' => $file
                    ]);
                }
            }

        } catch (Exception $e) {
            Log::warning("Error deleting existing profile image", [
                'member_id' => $memberId,
                'error' => $e->getMessage()
            ]);
        }
    }

    /**
     * Validate uploaded file
     *
     * @param UploadedFile $file
     * @param string $type ('image' or 'document')
     * @throws Exception
     */
    private function validateFile(UploadedFile $file, string $type): void
    {
        // Check if file is valid
        if (!$file->isValid()) {
            throw new Exception('Invalid file upload');
        }

        $extension = strtolower($file->getClientOriginalExtension());
        $allowedExtensions = $type === 'image' ? self::ALLOWED_IMAGE_EXTENSIONS : self::ALLOWED_DOCUMENT_EXTENSIONS;
        $maxSize = $type === 'image' ? self::MAX_IMAGE_SIZE : self::MAX_DOCUMENT_SIZE;

        // Check file extension
        if (!in_array($extension, $allowedExtensions)) {
            throw new Exception("File type not allowed. Allowed types: " . implode(', ', $allowedExtensions));
        }

        // Check file size
        if ($file->getSize() > ($maxSize * 1024)) {
            throw new Exception("File size exceeds maximum allowed size of {$maxSize}KB");
        }

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

    /**
     * Create member folder structure in S3
     *
     * @param int $memberId
     */
    public function createMemberFolders(int $memberId): void
    {
        try {
            $currentClient = session('currentClient', 'default');

            $folders = [
                $currentClient . '/members/' . $memberId . '/images/.gitkeep',
                $currentClient . '/members/' . $memberId . '/documents/.gitkeep',
                $currentClient . '/members/' . $memberId . '/certificates/.gitkeep'
            ];

            foreach ($folders as $folder) {
                if (!$this->disk->exists($folder)) {
                    $this->disk->put($folder, '');
                }
            }

            Log::info("Created member folder structure", ['member_id' => $memberId]);

        } catch (Exception $e) {
            Log::error("Error creating member folders", [
                'member_id' => $memberId,
                'error' => $e->getMessage()
            ]);
        }
    }

    /**
     * Get file info
     *
     * @param string $filePath
     * @return array|null
     */
    public function getFileInfo(string $filePath): ?array
    {
        if (!$filePath) {
            return null;
        }

        // Handle legacy local paths
        if (!$this->isS3Path($filePath)) {
            $localPath = storage_path('app/public/' . $filePath);
            if (file_exists($localPath)) {
                return [
                    'path' => $filePath,
                    'name' => basename($filePath),
                    'size' => filesize($localPath),
                    'last_modified' => filemtime($localPath),
                    'url' => $this->getFileUrl($filePath),
                    'exists' => true,
                    'storage_type' => 'local'
                ];
            }
            return null;
        }

        if (!$this->disk->exists($filePath)) {
            return null;
        }

        try {
            return [
                'path' => $filePath,
                'name' => basename($filePath),
                'size' => $this->disk->size($filePath),
                'last_modified' => $this->disk->lastModified($filePath),
                'url' => $this->getFileUrl($filePath),
                'exists' => true,
                'storage_type' => 's3'
            ];

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