import { Client } from 'minio';
import { config } from './index';
import { logger } from './logger';

/**
 * MinIO Client Configuration
 * Used for uploading videos and attachments to S3-compatible storage
 */
export const minioClient = new Client({
    endPoint: new URL(config.s3.endpoint).hostname,
    port: parseInt(new URL(config.s3.endpoint).port) || (config.s3.useSSL ? 443 : 9000),
    useSSL: config.s3.useSSL,
    accessKey: config.s3.accessKey,
    secretKey: config.s3.secretKey,
});

/**
 * Ensure bucket exists, create if not
 */
export async function ensureBucketExists(): Promise<void> {
    try {
        const exists = await minioClient.bucketExists(config.s3.bucket);
        if (!exists) {
            await minioClient.makeBucket(config.s3.bucket, 'us-east-1');
            logger.info(`Bucket '${config.s3.bucket}' created successfully`);
        }
    } catch (error) {
        logger.error(`Error ensuring bucket exists: ${error}`);
        throw error;
    }
}

/**
 * Generate a unique file path for storage
 */
export function generateFilePath(
    courseId: number,
    lessonId: number,
    fileType: 'video' | 'attachment',
    originalFilename: string
): string {
    const timestamp = Date.now();
    const uniqueId = Math.random().toString(36).substring(2, 15);
    const extension = originalFilename.split('.').pop() || '';
    const safeFilename = `${timestamp}-${uniqueId}.${extension}`;

    if (fileType === 'video') {
        return `courses/${courseId}/lessons/${lessonId}/video/${safeFilename}`;
    }
    return `courses/${courseId}/lessons/${lessonId}/attachments/${safeFilename}`;
}

/**
 * Upload a file to MinIO
 */
export async function uploadFile(
    filePath: string,
    fileBuffer: Buffer,
    mimeType: string
): Promise<string> {
    try {
        await ensureBucketExists();

        await minioClient.putObject(
            config.s3.bucket,
            filePath,
            fileBuffer,
            fileBuffer.length,
            { 'Content-Type': mimeType }
        );

        logger.info(`File uploaded successfully: ${filePath}`);
        return filePath;
    } catch (error) {
        logger.error(`Error uploading file: ${error}`);
        throw error;
    }
}

/**
 * Delete a file from MinIO
 */
export async function deleteFile(filePath: string): Promise<void> {
    try {
        await minioClient.removeObject(config.s3.bucket, filePath);
        logger.info(`File deleted successfully: ${filePath}`);
    } catch (error) {
        logger.error(`Error deleting file: ${error}`);
        throw error;
    }
}

/**
 * Get a presigned URL for file access
 */
export async function getPresignedUrl(
    filePath: string,
    expirySeconds: number = 3600
): Promise<string> {
    try {
        const url = await minioClient.presignedGetObject(
            config.s3.bucket,
            filePath,
            expirySeconds
        );
        return url;
    } catch (error) {
        logger.error(`Error generating presigned URL: ${error}`);
        throw error;
    }
}

/**
 * List all objects in a path prefix
 */
export async function listObjects(prefix: string): Promise<{ name: string; size: number; lastModified: Date }[]> {
    return new Promise((resolve, reject) => {
        const objects: { name: string; size: number; lastModified: Date }[] = [];
        const stream = minioClient.listObjectsV2(config.s3.bucket, prefix, true);

        stream.on('data', (obj) => {
            if (obj.name) {
                objects.push({
                    name: obj.name,
                    size: obj.size || 0,
                    lastModified: obj.lastModified || new Date(),
                });
            }
        });

        stream.on('error', (err) => {
            logger.error(`Error listing objects: ${err}`);
            reject(err);
        });

        stream.on('end', () => {
            resolve(objects);
        });
    });
}

/**
 * List folders in a path prefix
 * Uses listObjectsV2 with recursive=false to get folder prefixes
 */
export async function listFolders(prefix: string): Promise<string[]> {
    return new Promise((resolve, reject) => {
        const folders = new Set<string>();
        const stream = minioClient.listObjectsV2(config.s3.bucket, prefix, false);

        stream.on('data', (obj: any) => {
            if (obj.prefix) {
                // Direct folder prefix
                folders.add(obj.prefix);
            } else if (obj.name) {
                // Extract folder part from file path
                const path = obj.name.replace(prefix, '');
                const folder = path.split('/')[0];

                if (folder && path.includes('/')) {
                    folders.add(prefix + folder + '/');
                }
            }
        });

        stream.on('error', (err) => {
            logger.error(`Error listing folders: ${err}`);
            reject(err);
        });

        stream.on('end', () => {
            resolve(Array.from(folders));
        });
    });
}

/**
 * Get attachments folder path for a lesson
 */
export function getAttachmentsFolder(courseId: number, lessonId: number): string {
    return `courses/${courseId}/lessons/${lessonId}/attachments/`;
}

/**
 * Get video folder path for a lesson
 */
export function getVideoFolder(courseId: number, lessonId: number): string {
    return `courses/${courseId}/lessons/${lessonId}/video/`;
}
