import * as plugins from '../plugins.js';
import type * as interfaces from '../interfaces/index.js';
import type { TsView } from '../tsview.classes.tsview.js';

/**
 * Register S3 API handlers
 */
export async function registerS3Handlers(
  typedrouter: plugins.typedrequest.TypedRouter,
  tsview: TsView
): Promise<void> {
  console.log('Registering S3 handlers...');

  // List all buckets
  console.log('Registering listBuckets handler');
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_ListBuckets>(
      'listBuckets',
      async () => {
        console.log('listBuckets handler called');
        const smartbucket = await tsview.getSmartBucket();
        console.log('smartbucket:', smartbucket ? 'initialized' : 'null');
        if (!smartbucket) {
          console.log('returning empty buckets');
          return { buckets: [] };
        }

        try {
          const command = new plugins.s3.ListBucketsCommand({});
          console.log('sending ListBucketsCommand...');
          const response = await smartbucket.s3Client.send(command) as plugins.s3.ListBucketsCommandOutput;
          console.log('response:', response);
          const buckets = response.Buckets?.map(b => b.Name).filter((name): name is string => !!name) || [];
          console.log('returning buckets:', buckets);
          return { buckets };
        } catch (err) {
          console.error('Error listing buckets:', err);
          return { buckets: [] };
        }
      }
    )
  );

  // Create bucket
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_CreateBucket>(
      'createBucket',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          return { success: false };
        }

        try {
          await smartbucket.createBucket(reqData.bucketName);
          return { success: true };
        } catch (err) {
          console.error('Error creating bucket:', err);
          return { success: false };
        }
      }
    )
  );

  // Delete bucket
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_DeleteBucket>(
      'deleteBucket',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          return { success: false };
        }

        try {
          await smartbucket.removeBucket(reqData.bucketName);
          return { success: true };
        } catch (err) {
          console.error('Error deleting bucket:', err);
          return { success: false };
        }
      }
    )
  );

  // List objects in bucket
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_ListObjects>(
      'listObjects',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          return { objects: [], prefixes: [] };
        }

        try {
          const bucket = await smartbucket.getBucketByName(reqData.bucketName);
          if (!bucket) {
            return { objects: [], prefixes: [] };
          }

          const prefix = reqData.prefix || '';
          const delimiter = reqData.delimiter || '/';

          // Get the base directory or subdirectory
          const baseDir = await bucket.getBaseDirectory();
          let targetDir = baseDir;

          if (prefix) {
            // Navigate to the prefix directory
            const prefixParts = prefix.replace(/\/$/, '').split('/').filter(Boolean);
            for (const part of prefixParts) {
              const subDir = await targetDir.getSubDirectoryByName(part, { getEmptyDirectory: true });
              if (subDir) {
                targetDir = subDir;
              } else {
                return { objects: [], prefixes: [] };
              }
            }
          }

          const objects: interfaces.IStorageObject[] = [];
          const prefixSet = new Set<string>();

          // List files in current directory
          const files = await targetDir.listFiles();
          for (const file of files) {
            const fullPath = prefix + file.name;
            objects.push({
              key: fullPath,
              isPrefix: false,
            });
          }

          // List subdirectories
          const dirs = await targetDir.listDirectories();
          for (const dir of dirs) {
            const fullPrefix = prefix + dir.name + '/';
            prefixSet.add(fullPrefix);
          }

          return {
            objects,
            prefixes: Array.from(prefixSet),
          };
        } catch (err) {
          console.error('Error listing objects:', err);
          return { objects: [], prefixes: [] };
        }
      }
    )
  );

  // Get object content
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_GetObject>(
      'getObject',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          throw new Error('S3 not configured');
        }

        try {
          const bucket = await smartbucket.getBucketByName(reqData.bucketName);
          if (!bucket) {
            throw new Error(`Bucket ${reqData.bucketName} not found`);
          }

          const content = await bucket.fastGet({ path: reqData.key });
          const stats = await bucket.fastStat({ path: reqData.key });

          // Determine content type from extension
          const ext = reqData.key.split('.').pop()?.toLowerCase() || '';
          const contentTypeMap: Record<string, string> = {
            'json': 'application/json',
            'txt': 'text/plain',
            'html': 'text/html',
            'css': 'text/css',
            'js': 'application/javascript',
            'ts': 'text/plain',
            'tsx': 'text/plain',
            'jsx': 'text/plain',
            'md': 'text/markdown',
            'csv': 'text/csv',
            'yaml': 'text/yaml',
            'yml': 'text/yaml',
            'log': 'text/plain',
            'sh': 'text/plain',
            'env': 'text/plain',
            'png': 'image/png',
            'jpg': 'image/jpeg',
            'jpeg': 'image/jpeg',
            'gif': 'image/gif',
            'webp': 'image/webp',
            'svg': 'image/svg+xml',
            'pdf': 'application/pdf',
            'xml': 'application/xml',
          };
          const contentType = contentTypeMap[ext] || 'application/octet-stream';

          return {
            content: content.toString('base64'),
            contentType,
            size: stats?.ContentLength || content.length,
            lastModified: stats?.LastModified?.toISOString() || new Date().toISOString(),
          };
        } catch (err) {
          console.error('Error getting object:', err);
          throw err;
        }
      }
    )
  );

  // Get object metadata
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_GetObjectMetadata>(
      'getObjectMetadata',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          throw new Error('S3 not configured');
        }

        try {
          const bucket = await smartbucket.getBucketByName(reqData.bucketName);
          if (!bucket) {
            throw new Error(`Bucket ${reqData.bucketName} not found`);
          }

          const stats = await bucket.fastStat({ path: reqData.key });

          const ext = reqData.key.split('.').pop()?.toLowerCase() || '';
          const contentTypeMap: Record<string, string> = {
            'json': 'application/json',
            'txt': 'text/plain',
            'html': 'text/html',
            'css': 'text/css',
            'js': 'application/javascript',
            'ts': 'text/plain',
            'tsx': 'text/plain',
            'jsx': 'text/plain',
            'md': 'text/markdown',
            'csv': 'text/csv',
            'yaml': 'text/yaml',
            'yml': 'text/yaml',
            'log': 'text/plain',
            'sh': 'text/plain',
            'env': 'text/plain',
            'png': 'image/png',
            'jpg': 'image/jpeg',
            'jpeg': 'image/jpeg',
            'gif': 'image/gif',
            'webp': 'image/webp',
            'svg': 'image/svg+xml',
            'pdf': 'application/pdf',
            'xml': 'application/xml',
          };
          const contentType = contentTypeMap[ext] || 'application/octet-stream';

          return {
            contentType,
            size: stats?.ContentLength || 0,
            lastModified: stats?.LastModified?.toISOString() || new Date().toISOString(),
          };
        } catch (err) {
          console.error('Error getting object metadata:', err);
          throw err;
        }
      }
    )
  );

  // Put object
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_PutObject>(
      'putObject',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          return { success: false };
        }

        try {
          const bucket = await smartbucket.getBucketByName(reqData.bucketName);
          if (!bucket) {
            return { success: false };
          }

          const content = Buffer.from(reqData.content, 'base64');
          await bucket.fastPut({
            path: reqData.key,
            contents: content,
            overwrite: true,
          });

          return { success: true };
        } catch (err) {
          console.error('Error putting object:', err);
          return { success: false };
        }
      }
    )
  );

  // Delete object
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_DeleteObject>(
      'deleteObject',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          return { success: false };
        }

        try {
          const bucket = await smartbucket.getBucketByName(reqData.bucketName);
          if (!bucket) {
            return { success: false };
          }

          await bucket.fastRemove({ path: reqData.key });
          return { success: true };
        } catch (err) {
          console.error('Error deleting object:', err);
          return { success: false };
        }
      }
    )
  );

  // Copy object
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_CopyObject>(
      'copyObject',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          return { success: false };
        }

        try {
          const sourceBucket = await smartbucket.getBucketByName(reqData.sourceBucket);
          const destBucket = await smartbucket.getBucketByName(reqData.destBucket);

          if (!sourceBucket || !destBucket) {
            return { success: false };
          }

          // Read from source
          const content = await sourceBucket.fastGet({ path: reqData.sourceKey });

          // Write to destination
          await destBucket.fastPut({
            path: reqData.destKey,
            contents: content,
            overwrite: true,
          });

          return { success: true };
        } catch (err) {
          console.error('Error copying object:', err);
          return { success: false };
        }
      }
    )
  );

  // Delete prefix (folder and all contents)
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_DeletePrefix>(
      'deletePrefix',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          return { success: false };
        }

        try {
          const bucket = await smartbucket.getBucketByName(reqData.bucketName);
          if (!bucket) {
            return { success: false };
          }

          const baseDir = await bucket.getBaseDirectory();
          let targetDir = baseDir;

          // Navigate to the prefix directory
          const prefix = reqData.prefix.replace(/\/$/, '');
          const prefixParts = prefix.split('/').filter(Boolean);
          for (const part of prefixParts) {
            const subDir = await targetDir.getSubDirectoryByName(part, { getEmptyDirectory: true });
            if (subDir) {
              targetDir = subDir;
            } else {
              return { success: false };
            }
          }

          // Delete the directory and all its contents
          await targetDir.delete({ mode: 'permanent' });
          return { success: true };
        } catch (err) {
          console.error('Error deleting prefix:', err);
          return { success: false };
        }
      }
    )
  );

  // Get object URL (for downloads)
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_GetObjectUrl>(
      'getObjectUrl',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          throw new Error('S3 not configured');
        }

        try {
          const bucket = await smartbucket.getBucketByName(reqData.bucketName);
          if (!bucket) {
            throw new Error(`Bucket ${reqData.bucketName} not found`);
          }

          // Get the content and create a data URL
          const content = await bucket.fastGet({ path: reqData.key });
          const ext = reqData.key.split('.').pop()?.toLowerCase() || '';
          const contentTypeMap: Record<string, string> = {
            'json': 'application/json',
            'txt': 'text/plain',
            'html': 'text/html',
            'css': 'text/css',
            'js': 'application/javascript',
            'ts': 'text/plain',
            'tsx': 'text/plain',
            'jsx': 'text/plain',
            'md': 'text/markdown',
            'csv': 'text/csv',
            'yaml': 'text/yaml',
            'yml': 'text/yaml',
            'log': 'text/plain',
            'sh': 'text/plain',
            'env': 'text/plain',
            'png': 'image/png',
            'jpg': 'image/jpeg',
            'jpeg': 'image/jpeg',
            'gif': 'image/gif',
            'webp': 'image/webp',
            'svg': 'image/svg+xml',
            'pdf': 'application/pdf',
            'xml': 'application/xml',
          };
          const contentType = contentTypeMap[ext] || 'application/octet-stream';
          const base64 = content.toString('base64');
          const url = `data:${contentType};base64,${base64}`;

          return { url };
        } catch (err) {
          console.error('Error getting object URL:', err);
          throw err;
        }
      }
    )
  );

  // Move object (copy + delete)
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_MoveObject>(
      'moveObject',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          return { success: false, error: 'S3 not configured' };
        }

        try {
          const bucket = await smartbucket.getBucketByName(reqData.bucketName);
          if (!bucket) {
            return { success: false, error: `Bucket ${reqData.bucketName} not found` };
          }

          // Read source content
          const content = await bucket.fastGet({ path: reqData.sourceKey });

          // Write to destination
          await bucket.fastPut({
            path: reqData.destKey,
            contents: content,
            overwrite: true,
          });

          // Delete source
          await bucket.fastRemove({ path: reqData.sourceKey });

          return { success: true };
        } catch (err) {
          console.error('Error moving object:', err);
          return { success: false, error: String(err) };
        }
      }
    )
  );

  // Move prefix (folder) - copy all objects then delete all
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_MovePrefix>(
      'movePrefix',
      async (reqData) => {
        const smartbucket = await tsview.getSmartBucket();
        if (!smartbucket) {
          return { success: false, movedCount: 0, error: 'S3 not configured' };
        }

        try {
          const bucket = await smartbucket.getBucketByName(reqData.bucketName);
          if (!bucket) {
            return { success: false, movedCount: 0, error: `Bucket ${reqData.bucketName} not found` };
          }

          // List all objects under the source prefix recursively
          const allObjects: string[] = [];

          const listRecursive = async (prefix: string): Promise<void> => {
            const baseDir = await bucket.getBaseDirectory();
            let targetDir = baseDir;

            if (prefix) {
              const prefixParts = prefix.replace(/\/$/, '').split('/').filter(Boolean);
              for (const part of prefixParts) {
                const subDir = await targetDir.getSubDirectoryByName(part, { getEmptyDirectory: true });
                if (subDir) {
                  targetDir = subDir;
                } else {
                  return;
                }
              }
            }

            // Get files in this directory
            const files = await targetDir.listFiles();
            for (const file of files) {
              allObjects.push(prefix + file.name);
            }

            // Recurse into subdirectories
            const dirs = await targetDir.listDirectories();
            for (const dir of dirs) {
              await listRecursive(prefix + dir.name + '/');
            }
          };

          await listRecursive(reqData.sourcePrefix);

          // Copy all objects to new location
          for (const objKey of allObjects) {
            const relativePath = objKey.substring(reqData.sourcePrefix.length);
            const newKey = reqData.destPrefix + relativePath;

            const content = await bucket.fastGet({ path: objKey });
            await bucket.fastPut({
              path: newKey,
              contents: content,
              overwrite: true,
            });
          }

          // Delete the source directory
          const baseDir = await bucket.getBaseDirectory();
          let targetDir = baseDir;
          const prefix = reqData.sourcePrefix.replace(/\/$/, '');
          const prefixParts = prefix.split('/').filter(Boolean);

          for (const part of prefixParts) {
            const subDir = await targetDir.getSubDirectoryByName(part, { getEmptyDirectory: true });
            if (subDir) {
              targetDir = subDir;
            } else {
              return { success: false, movedCount: 0, error: 'Source folder not found' };
            }
          }

          await targetDir.delete({ mode: 'permanent' });

          return { success: true, movedCount: allObjects.length };
        } catch (err) {
          console.error('Error moving prefix:', err);
          return { success: false, movedCount: 0, error: String(err) };
        }
      }
    )
  );
}
