import { Application } from 'express';
import { Resource } from 'express-automatic-routes';
import path from 'path';
import { FileProvider } from '#providers/FileProvider';
import { Req, Res } from '#interfaces/IApi';
import { Errors } from '#interfaces/error';
import verify, { attachPermissions } from '#middlewares/auth';
import { validateId } from '#middlewares/validators';
import { getStorageAdapter } from '#services/storage/adapters/StorageFactory';
import Config from '#config';

export default (_express: Application) => {
	const fileProvider = FileProvider.getInstance();
	return <Resource>{
		get: {
			middleware: [verify, attachPermissions, validateId],
			handler: async (req: Req, res: Res) => {
				try {
					const file = await fileProvider.getById({ id: req.params.id! });

					// Treat soft-deleted files as not found
					if (!file) {
						throw Errors.fileNotFound();
					}

					// Check if file is soft-deleted
					if (file.deleted_at !== null) {
						throw Errors.fileNotFound();
					}

					const userId = req.user?.id;
					const userRoles: string[] = req.user?.roles ?? [];
					const userPermissions: string[] = req.user?.permissions ?? [];
					const isAdmin = userRoles.includes('admin') || userRoles.includes('system_admin');
					const hasPermission = userPermissions.includes('read:file') || userPermissions.includes('read_file');
					const isOwner = file.owner_user_id != null && file.owner_user_id === userId;

					if (!isAdmin && !hasPermission && !isOwner) {
						throw Errors.forbidden();
					}

					// Get mode from query param (default: view for preview)
					const mode = req.query.mode === 'download' ? 'download' : 'view';
					const disposition = mode === 'download' ? 'attachment' : 'inline';

					const storageProvider = file.provider;
					const storagePath: string = file.object_key;

					// For object-storage providers, redirect to a short-lived signed URL
					if (storageProvider === 'minio' || storageProvider === 's3') {
						const storageAdapter = getStorageAdapter();
						const signedUrl = await storageAdapter.getSignedUrl(storagePath, 300, disposition); // 5-min TTL
						if (!signedUrl) {
							throw Errors.internalError();
						}
						return res.redirect(302, signedUrl);
					}

					// Local storage: stream the file directly from disk
					const storageRoot = path.resolve(Config.storage.localStorageRoot, '');

					let absolutePath: string;
					if (path.isAbsolute(storagePath)) {
						absolutePath = storagePath;
					} else {
						// Strip legacy /api/storage/uploads prefix and any leading slash
						const normalizedKey = storagePath.replace(/^\/api\/storage\/uploads\/?/, '').replace(/^\/+/, '');
						absolutePath = path.resolve(storageRoot, normalizedKey);
					}

					// P0-3: guard against path traversal - strict check to prevent accessing storageRoot itself
					if (!absolutePath.startsWith(storageRoot + path.sep)) {
						throw Errors.forbidden();
					}

					const downloadName: string = file.original_name ?? path.basename(absolutePath);

					res.setHeader('Content-Disposition', `${disposition}; filename="${encodeURIComponent(downloadName)}"`);
					if (file.mime_type) {
						res.setHeader('Content-Type', file.mime_type);
					}

					return res.sendFile(absolutePath);
				} catch (error) {
					return res.error(error);
				}
			},
		},
	};
};
