import { listOrgResources } from '@agentuity/server';
import { basename } from 'path';
import { z } from 'zod';
import { getResourceInfo, setResourceInfo } from '../../../cache';
import { getCommand } from '../../../command-prefix';
import { getGlobalCatalystAPIClient } from '../../../config';
import { ErrorCode } from '../../../errors';
import * as tui from '../../../tui';
import { createSubcommand } from '../../../types';
import { createS3Client } from './utils';
export const uploadSubcommand = createSubcommand({
    name: 'upload',
    aliases: ['put'],
    description: 'Upload a file to storage bucket',
    tags: ['write', 'requires-auth', 'uses-stdin'],
    requires: { auth: true },
    optional: { org: true },
    idempotent: false,
    examples: [
        {
            command: `${getCommand('cloud storage upload')} my-bucket file.txt`,
            description: 'Upload file to bucket',
        },
        {
            command: `${getCommand('cloud storage put')} my-bucket file.txt --content-type text/plain`,
            description: 'Upload file with content type',
        },
        {
            command: `${getCommand('cloud storage upload')} my-bucket file.txt --key custom-name.txt`,
            description: 'Upload file with custom object key',
        },
        {
            command: `cat file.txt | ${getCommand('cloud storage upload')} my-bucket -`,
            description: 'Upload from stdin',
        },
        {
            command: `cat data.json | ${getCommand('cloud storage upload')} my-bucket - --key data.json`,
            description: 'Upload from stdin with custom key',
        },
    ],
    schema: {
        args: z.object({
            name: z.string().describe('Bucket name'),
            filename: z.string().describe('File path to upload or "-" for STDIN'),
        }),
        options: z.object({
            key: z
                .string()
                .optional()
                .describe('Remote object key (defaults to basename or "stdin" for piped uploads)'),
            contentType: z
                .string()
                .optional()
                .describe('Content type (auto-detected if not provided)'),
        }),
        response: z.object({
            success: z.boolean().describe('Whether upload succeeded'),
            bucket: z.string().describe('Bucket name'),
            filename: z.string().describe('Uploaded filename'),
            size: z.number().describe('File size in bytes'),
        }),
    },
    async handler(ctx) {
        const { logger, args, opts, options, auth, config } = ctx;
        const profileName = config?.name ?? 'production';
        const catalystClient = await getGlobalCatalystAPIClient(logger, auth, profileName, undefined, config);
        // Check cache first for orgId
        const cachedInfo = await getResourceInfo('bucket', profileName, args.name);
        const orgId = ctx.orgId ?? cachedInfo?.orgId;
        if (!orgId) {
            tui.fatal(`Organization not found for bucket '${args.name}'. Run 'agentuity cloud storage list' first or specify --org-id.`, ErrorCode.INVALID_ARGUMENT);
        }
        // Fetch bucket credentials
        const resources = await tui.spinner({
            message: `Fetching credentials for ${args.name}`,
            clearOnSuccess: true,
            callback: async () => {
                return listOrgResources(catalystClient, { type: 's3', orgId });
            },
        });
        const bucket = resources.s3.find((s3) => s3.bucket_name === args.name);
        // Cache the bucket info for future lookups
        if (bucket?.cloud_region) {
            await setResourceInfo('bucket', profileName, bucket.bucket_name, bucket.cloud_region, orgId);
        }
        if (!bucket) {
            tui.fatal(`Storage bucket '${args.name}' not found`, ErrorCode.RESOURCE_NOT_FOUND);
        }
        if (!bucket.access_key || !bucket.secret_key || !bucket.endpoint) {
            tui.fatal(`Storage bucket '${args.name}' is missing credentials`, ErrorCode.CONFIG_INVALID);
        }
        // Prepare streaming upload - we don't buffer the entire file in memory
        let stream;
        if (args.filename === '-') {
            // Stream from STDIN
            stream = Bun.stdin.stream();
        }
        else {
            // Stream from file
            const file = Bun.file(args.filename);
            if (!(await file.exists())) {
                tui.fatal(`File not found: ${args.filename}`, ErrorCode.FILE_NOT_FOUND);
            }
            stream = file.stream();
        }
        // Derive the remote object key:
        // 1. Use --key if provided
        // 2. For stdin (-), default to 'stdin'
        // 3. For files, use the basename
        const objectKey = opts.key && opts.key.trim().length > 0
            ? opts.key
            : args.filename === '-'
                ? 'stdin'
                : basename(args.filename);
        // Auto-detect content type from the object key's extension
        // This allows content-type detection for stdin when --key is provided
        let contentType = opts.contentType;
        if (!contentType) {
            const dotIndex = objectKey.lastIndexOf('.');
            const ext = dotIndex > 0 ? objectKey.substring(dotIndex + 1).toLowerCase() : undefined;
            const mimeTypes = {
                txt: 'text/plain',
                html: 'text/html',
                css: 'text/css',
                yaml: 'application/x-yaml',
                yml: 'application/x-yaml',
                js: 'application/javascript',
                json: 'application/json',
                xml: 'application/xml',
                pdf: 'application/pdf',
                zip: 'application/zip',
                jpg: 'image/jpeg',
                jpeg: 'image/jpeg',
                png: 'image/png',
                gif: 'image/gif',
                svg: 'image/svg+xml',
                mp4: 'video/mp4',
                mp3: 'audio/mpeg',
            };
            contentType = ext ? mimeTypes[ext] : 'application/octet-stream';
        }
        // Upload using Bun.s3
        const s3Client = createS3Client({
            endpoint: bucket.endpoint,
            access_key: bucket.access_key,
            secret_key: bucket.secret_key,
            region: bucket.region,
        });
        // Upload using streaming - wrap the stream in a Response object
        // S3Client.write accepts Response which allows streaming without buffering in memory
        let bytesUploaded = 0;
        await tui.spinner({
            message: `Uploading ${objectKey} to ${args.name}`,
            clearOnSuccess: true,
            callback: async () => {
                bytesUploaded = await s3Client.write(objectKey, new Response(stream), {
                    type: contentType,
                });
            },
        });
        if (!options.json) {
            tui.success(`Uploaded ${tui.bold(objectKey)} to ${tui.bold(args.name)} (${bytesUploaded} bytes)`);
        }
        return {
            success: true,
            bucket: args.name,
            filename: objectKey,
            size: bytesUploaded,
        };
    },
});
//# sourceMappingURL=upload.js.map