/**
 * Local filesystem asset backend
 *
 * Stores assets in .studiograph/assets/ directory
 */
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, unlinkSync, statSync, copyFileSync } from 'fs';
import { join, dirname, relative } from 'path';
import { getMediaType } from './base.js';
export class LocalAssetBackend {
    basePath;
    constructor(basePath) {
        this.basePath = basePath;
        this.ensureBaseDirectory();
    }
    /**
     * Ensure base directory exists
     */
    ensureBaseDirectory() {
        if (!existsSync(this.basePath)) {
            mkdirSync(this.basePath, { recursive: true });
        }
    }
    /**
     * Get full file path for a key
     */
    getFilePath(key) {
        return join(this.basePath, key);
    }
    /**
     * Upload asset to local filesystem
     */
    async upload(key, buffer, contentType) {
        const filePath = this.getFilePath(key);
        const dir = dirname(filePath);
        // Ensure directory exists
        if (!existsSync(dir)) {
            mkdirSync(dir, { recursive: true });
        }
        // Write file
        writeFileSync(filePath, buffer);
        // Get file size
        const stats = statSync(filePath);
        const filename = key.split('/').pop() || key;
        // Generate relative path for markdown
        // For local files, we use relative paths from workspace root
        const relativePath = relative(process.cwd(), filePath);
        return {
            filename,
            size: stats.size,
            contentType,
            url: `file:///${filePath}`, // Local file URL
            path: relativePath, // Relative path for markdown
            mediaType: getMediaType(contentType, filename),
        };
    }
    /**
     * List assets with a given prefix
     */
    async list(prefix) {
        const dirPath = this.getFilePath(prefix);
        if (!existsSync(dirPath)) {
            return [];
        }
        const assets = [];
        try {
            const files = readdirSync(dirPath);
            for (const file of files) {
                const filePath = join(dirPath, file);
                const stats = statSync(filePath);
                // Skip directories
                if (stats.isDirectory()) {
                    continue;
                }
                const key = join(prefix, file);
                const filename = file;
                const relativePath = relative(process.cwd(), filePath);
                // Try to detect content type from filename
                const ext = filename.split('.').pop()?.toLowerCase() || '';
                const contentType = this.guessContentType(ext);
                assets.push({
                    filename,
                    size: stats.size,
                    contentType,
                    url: `file:///${filePath}`,
                    path: relativePath,
                    mediaType: getMediaType(contentType, filename),
                });
            }
        }
        catch (error) {
            // Directory doesn't exist or can't be read
            return [];
        }
        return assets;
    }
    /**
     * List every asset under basePath in a single filesystem walk. Parses
     * the `{type}/{id}/{filename}` layout without any markdown or metadata
     * reads. Used by the asset manager endpoint.
     */
    async listAll() {
        if (!existsSync(this.basePath))
            return [];
        const results = [];
        let types = [];
        try {
            types = readdirSync(this.basePath);
        }
        catch {
            return [];
        }
        for (const type of types) {
            if (type.startsWith('.'))
                continue;
            const typeDir = join(this.basePath, type);
            try {
                if (!statSync(typeDir).isDirectory())
                    continue;
            }
            catch {
                continue;
            }
            let ids = [];
            try {
                ids = readdirSync(typeDir);
            }
            catch {
                continue;
            }
            for (const id of ids) {
                if (id.startsWith('.'))
                    continue;
                const idDir = join(typeDir, id);
                try {
                    if (!statSync(idDir).isDirectory())
                        continue;
                }
                catch {
                    continue;
                }
                let files = [];
                try {
                    files = readdirSync(idDir);
                }
                catch {
                    continue;
                }
                for (const filename of files) {
                    if (filename.startsWith('.'))
                        continue;
                    const filePath = join(idDir, filename);
                    let stats;
                    try {
                        stats = statSync(filePath);
                    }
                    catch {
                        continue;
                    }
                    if (!stats.isFile())
                        continue;
                    const ext = filename.split('.').pop()?.toLowerCase() || '';
                    const contentType = this.guessContentType(ext);
                    const relativePath = relative(process.cwd(), filePath);
                    results.push({
                        filename,
                        size: stats.size,
                        contentType,
                        url: `file:///${filePath}`,
                        path: relativePath,
                        mediaType: getMediaType(contentType, filename),
                        entityType: type,
                        entityId: id,
                    });
                }
            }
        }
        return results;
    }
    /**
     * Server-side copy (filesystem copy). Creates the destination directory
     * and overwrites an existing destination so migration re-runs are idempotent.
     */
    /**
     * Read an object's bytes from the local filesystem.
     */
    async download(key) {
        const filePath = this.getFilePath(key);
        if (!existsSync(filePath)) {
            throw new Error(`Asset not found: ${key}`);
        }
        return readFileSync(filePath);
    }
    async copy(srcKey, destKey) {
        const srcPath = this.getFilePath(srcKey);
        if (!existsSync(srcPath)) {
            throw new Error(`Asset not found: ${srcKey}`);
        }
        const destPath = this.getFilePath(destKey);
        const destDir = dirname(destPath);
        if (!existsSync(destDir)) {
            mkdirSync(destDir, { recursive: true });
        }
        copyFileSync(srcPath, destPath);
    }
    /**
     * Delete a single asset
     */
    async delete(key) {
        const filePath = this.getFilePath(key);
        if (!existsSync(filePath)) {
            throw new Error(`Asset not found: ${key}`);
        }
        unlinkSync(filePath);
    }
    /**
     * Delete all assets with a given prefix
     */
    async deleteAll(prefix) {
        const dirPath = this.getFilePath(prefix);
        if (!existsSync(dirPath)) {
            return 0;
        }
        let count = 0;
        try {
            const files = readdirSync(dirPath);
            for (const file of files) {
                const filePath = join(dirPath, file);
                const stats = statSync(filePath);
                // Only delete files, not directories
                if (!stats.isDirectory()) {
                    unlinkSync(filePath);
                    count++;
                }
            }
        }
        catch (error) {
            throw new Error(`Failed to delete assets: ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
        return count;
    }
    /**
     * Check if an asset exists
     */
    async exists(key) {
        const filePath = this.getFilePath(key);
        return existsSync(filePath);
    }
    /**
     * Guess content type from file extension
     */
    guessContentType(ext) {
        const contentTypes = {
            png: 'image/png',
            jpg: 'image/jpeg',
            jpeg: 'image/jpeg',
            gif: 'image/gif',
            webp: 'image/webp',
            svg: 'image/svg+xml',
            mp4: 'video/mp4',
            mov: 'video/quicktime',
            webm: 'video/webm',
            mp3: 'audio/mpeg',
            wav: 'audio/wav',
            pdf: 'application/pdf',
            zip: 'application/zip',
        };
        return contentTypes[ext] || 'application/octet-stream';
    }
}
//# sourceMappingURL=local.js.map