/**
 * Brand Asset Routes
 *
 * Supports both file upload (multipart) and URL-based asset creation.
 * Images are stored on disk in uploads/brand-assets/ with only the
 * file path saved in MongoDB — no base64Data in the database.
 */

import express, { Request, Response } from 'express';
import { body, validationResult } from 'express-validator';
import { getModels } from '../models';
import { authenticateJwtOrApiToken } from '../middleware/dualAuth';
import { requirePermission, resolvePermission } from '../middleware/permissions';
import { uploadBrandAsset } from '../middleware/upload';
import { saveBrandAssetFile, deleteBrandAssetFile, base64ToBuffer, getExtensionFromMime } from '../utils/fileStorage';
import { getAvailableFormats, getMimeType, getExtension, isGuidelinesType, supportsTransparentBackground } from '../utils/assetFormats';
import { getConvertedAsset, getAiCutoutAsset, streamOriginalFile, sanitizeFilename, deleteConvertedFiles } from '../utils/assetConversion';

const router = express.Router();

router.use(authenticateJwtOrApiToken);

// ─── READ ────────────────────────────────────────────────────────────────────

// Get single asset (full data — must be before /:companyId)
router.get('/detail/:id', async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { BrandAsset } = getModels();

    const asset = await BrandAsset.findById(id).lean();
    if (!asset) {
      res.status(404).json({ error: 'Asset not found' });
      return;
    }

    if (!(req.user?.companyIds?.includes(asset.companyId)) && req.user?.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    res.json(asset);
  } catch (error) {
    console.error('[BrandAssets] Get detail error:', error);
    res.status(500).json({ error: 'Failed to get asset' });
  }
});

// Get base64Data for a single asset (legacy — for pre-migration records)
router.get('/base64/:id', async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { BrandAsset } = getModels();

    const asset = await BrandAsset.findById(id).select('base64Data companyId url').lean();
    if (!asset) {
      res.status(404).json({ error: 'Asset not found' });
      return;
    }

    if (!(req.user?.companyIds?.includes(asset.companyId)) && req.user?.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // New records have a URL instead of base64Data
    res.json({ base64Data: asset.base64Data || null, url: asset.url || null });
  } catch (error) {
    console.error('[BrandAssets] Get base64 error:', error);
    res.status(500).json({ error: 'Failed to get asset image' });
  }
});

// ─── DOWNLOAD (format conversion) ────────────────────────────────────────────

// Download asset in a specific format (PNG, JPG, SVG, ICO)
// Must be before /:companyId to avoid Express treating "download" as a company ID
router.get('/:id/download', async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const requestedFormat = ((req.query.format as string) || 'png').toLowerCase();
    // JPEG and JPG are the same output format; normalise so both are accepted.
    const format = requestedFormat === 'jpeg' ? 'jpg' : requestedFormat;

    const { BrandAsset } = getModels();

    // Also fetch base64Data for legacy records that don't have files on disk
    const asset = await BrandAsset.findById(id).lean();
    if (!asset) {
      res.status(404).json({ error: 'Asset not found' });
      return;
    }

    // Auth check
    if (!(req.user?.companyIds?.includes(asset.companyId)) && req.user?.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // Guidelines types don't support image downloads
    if (isGuidelinesType(asset.type) || asset.format === 'content') {
      res.status(400).json({ error: 'This asset type does not support image downloads' });
      return;
    }

    // Validate requested format
    const availableFormats = getAvailableFormats(asset.type, asset.format);
    if (!availableFormats.includes(format)) {
      res.status(400).json({
        error: `Format '${format}' not available for asset type '${asset.type}'. Available: ${availableFormats.join(', ')}`,
      });
      return;
    }

    // Explicit AI cut-out. Only meaningful for PNG — it is the one download
    // format that can carry transparency. This is a paid provider call, so it is
    // opt-in per request, gated on the same permission as any other AI generation,
    // and cached on disk so a given asset is only ever cut out once.
    if (String(req.query.removeBackground || '').toLowerCase() === 'ai' && format === 'png') {
      const companyId = req.user!.activeCompanyId || req.user!.companyIds?.[0] || '';
      const allowed =
        req.user!.role === 'super-admin' ||
        req.user!.role === 'admin' ||
        (await resolvePermission(req.user!._id.toString(), companyId, 'ai-processing', 'ai-generate'));
      if (!allowed) {
        res.status(403).json({ error: 'You do not have permission to run AI background removal' });
        return;
      }

      try {
        const cutout = await getAiCutoutAsset(asset, req.user?._id?.toString());
        if (!cutout) {
          res.status(404).json({ error: 'Source file not found. The asset may need to be re-uploaded.' });
          return;
        }
        const cutoutName = `${sanitizeFilename(asset.name || 'asset')}.png`;
        res.setHeader('Content-Type', cutout.mimeType);
        res.setHeader('Content-Disposition', `attachment; filename="${cutoutName}"`);
        res.setHeader('Content-Length', cutout.buffer.length);
        res.send(cutout.buffer);
      } catch (err: any) {
        console.error('[BrandAssets] AI background removal failed:', err);
        res.status(502).json({
          error: err?.message || 'AI background removal failed. Please try again.',
        });
      }
      return;
    }

    // If the requested format matches the stored format, stream the original file.
    // A PNG of a cut-out type is excluded: it must come out with a transparent
    // background, which only the conversion path applies.
    const storedFormat = asset.format || 'png';
    const needsBackgroundRemoval =
      format === 'png' && supportsTransparentBackground(asset.type);
    if (!needsBackgroundRemoval && (format === storedFormat || (format === 'jpg' && storedFormat === 'jpeg') || (format === 'jpg' && storedFormat === 'jpg'))) {
      // Try streaming from disk first
      const stream = streamOriginalFile(asset);
      if (stream) {
        const filename = `${sanitizeFilename(asset.name || 'asset')}.${getExtension(format)}`;
        res.setHeader('Content-Type', getMimeType(format));
        res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
        stream.pipe(res);
        return;
      }
      // Fall through to conversion (handles legacy base64Data records)
    }

    // Convert to the requested format
    const result = await getConvertedAsset(asset, format);
    if (!result) {
      res.status(404).json({ error: 'Source file not found. The asset may need to be re-uploaded.' });
      return;
    }

    const filename = `${sanitizeFilename(asset.name || 'asset')}.${result.extension}`;
    res.setHeader('Content-Type', result.mimeType);
    res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
    res.setHeader('Content-Length', result.buffer.length);
    res.send(result.buffer);
  } catch (error) {
    console.error('[BrandAssets] Download error:', error);
    res.status(500).json({ error: 'Failed to download asset' });
  }
});

// Get all assets for a company (excludes heavy base64Data)
router.get('/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    const { BrandAsset } = getModels();

    if (!(req.user?.companyIds?.includes(companyId)) && req.user?.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const assets = await BrandAsset.find({ companyId })
      .select('-base64Data')
      .sort({ createdAt: -1 })
      .lean();
    res.json(assets);
  } catch (error) {
    console.error('[BrandAssets] Get error:', error);
    res.status(500).json({ error: 'Failed to get brand assets' });
  }
});

// ─── CREATE ───────────────────────────────────────────────────────────────────

// Create asset from file upload (multipart/form-data)
router.post(
  '/upload',
  requirePermission('brand-assets', 'upload'),
  uploadBrandAsset.single('file'),
  async (req: Request, res: Response) => {
    try {
      const file = req.file as Express.Multer.File | undefined;
      if (!file) {
        res.status(400).json({ error: 'No file uploaded' });
        return;
      }

      const { companyId, name, type, description, sourceUrl, tags, isPrimary, format, founderId, employeeId } = req.body;

      if (!companyId || !name || !type) {
        // Clean up uploaded file on validation error
        await deleteBrandAssetFile(`/uploads/brand-assets/${file.filename}`);
        res.status(400).json({ error: 'companyId, name, and type are required' });
        return;
      }

      if (!(req.user?.companyIds?.includes(companyId)) && req.user?.role !== 'admin') {
        await deleteBrandAssetFile(`/uploads/brand-assets/${file.filename}`);
        res.status(403).json({ error: 'Access denied' });
        return;
      }

      const { BrandAsset } = getModels();

      // If setting as primary, unset other primary assets of same type
      if (isPrimary === 'true' || isPrimary === true) {
        await BrandAsset.updateMany(
          { companyId, type },
          { isPrimary: false }
        );
      }

      // Determine format from file mimetype
      const fileFormat = format || getExtensionFromMime(file.mimetype, 'png');

      // Parse tags if sent as JSON string
      let parsedTags: string[] = [];
      if (tags) {
        try {
          parsedTags = typeof tags === 'string' ? JSON.parse(tags) : tags;
        } catch {
          parsedTags = [];
        }
      }

      const asset = new BrandAsset({
        companyId,
        name,
        type,
        description: description || '',
        format: fileFormat,
        url: `/uploads/brand-assets/${file.filename}`,
        fileName: file.originalname,
        fileSize: file.size,
        fileType: file.mimetype,
        source: 'upload',
        sourceUrl: sourceUrl || '',
        tags: parsedTags,
        isPrimary: isPrimary === 'true' || isPrimary === true,
        founderId: founderId || undefined,
        employeeId: employeeId || undefined,
      });

      await asset.save();
      res.status(201).json(asset);
    } catch (error: any) {
      console.error('[BrandAssets] Upload create error:', error);
      if (error.name === 'ValidationError') {
        res.status(400).json({ error: error.message, details: Object.values(error.errors).map((e: any) => e.message) });
        return;
      }
      res.status(500).json({ error: 'Failed to create brand asset' });
    }
  }
);

// Create asset from JSON (URL-based or legacy base64)
router.post(
  '/',
  requirePermission('brand-assets', 'create'),
  [
    body('name').trim().notEmpty().withMessage('Asset name is required'),
    body('companyId').notEmpty().withMessage('Company ID is required'),
    body('type').notEmpty().withMessage('Asset type is required'),
    body('format').notEmpty().withMessage('Format is required'),
  ],
  async (req: Request, res: Response) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        res.status(400).json({ errors: errors.array() });
        return;
      }

      if (!(req.user?.companyIds?.includes(req.body.companyId)) && req.user?.role !== 'admin') {
        res.status(403).json({ error: 'Access denied' });
        return;
      }

      const { BrandAsset } = getModels();

      // If base64Data is present, convert it to a file and store the URL instead
      let assetData: Record<string, any> = { ...req.body };
      if (assetData.base64Data) {
        try {
          const { buffer, mimeType } = base64ToBuffer(assetData.base64Data);
          const ext = getExtensionFromMime(mimeType, assetData.format || 'png');
          const { url: fileUrl, fileSize } = await saveBrandAssetFile(buffer, `asset.${ext}`, mimeType);
          assetData.url = fileUrl;
          assetData.fileSize = assetData.fileSize || fileSize;
          assetData.fileType = assetData.fileType || mimeType;
          assetData.source = assetData.source || 'upload';
          // Remove base64Data from the document — we store files, not inline data
          delete assetData.base64Data;
        } catch (err) {
          console.error('[BrandAssets] Failed to save base64 to file:', err);
          // Fall through — the base64Data will be stored in DB as before (legacy path)
        }
      }

      // If setting as primary, unset other primary assets of same type
      if (assetData.isPrimary) {
        await BrandAsset.updateMany(
          { companyId: assetData.companyId, type: assetData.type },
          { isPrimary: false }
        );
      }

      const asset = new BrandAsset(assetData);
      await asset.save();

      res.status(201).json(asset);
    } catch (error: any) {
      console.error('[BrandAssets] Create error:', error);
      if (error.name === 'ValidationError') {
        res.status(400).json({ error: error.message, details: Object.values(error.errors).map((e: any) => e.message) });
        return;
      }
      res.status(500).json({ error: 'Failed to create brand asset' });
    }
  }
);

// ─── UPDATE ───────────────────────────────────────────────────────────────────

// Update asset with file upload (multipart/form-data)
router.put(
  '/:id/upload',
  requirePermission('brand-assets', 'edit'),
  uploadBrandAsset.single('file'),
  async (req: Request, res: Response) => {
    try {
      const { id } = req.params;
      const { BrandAsset } = getModels();

      const asset = await BrandAsset.findById(id);
      if (!asset) {
        res.status(404).json({ error: 'Asset not found' });
        return;
      }

      if (!(req.user?.companyIds?.includes(asset.companyId)) && req.user?.role !== 'admin') {
        res.status(403).json({ error: 'Access denied' });
        return;
      }

      const file = req.file as Express.Multer.File | undefined;
      if (!file) {
        res.status(400).json({ error: 'No file uploaded' });
        return;
      }

      // Delete the old file if it was stored locally
      if (asset.url && asset.url.startsWith('/uploads/brand-assets/')) {
        await deleteBrandAssetFile(asset.url);
        // Also clean up any cached format conversions
        deleteConvertedFiles(asset.url);
      }

      const { companyId, name, type, description, sourceUrl, tags, isPrimary, format, founderId, employeeId } = req.body;

      // If setting as primary, unset other primary assets of same type
      if ((isPrimary === 'true' || isPrimary === true) && !asset.isPrimary) {
        await BrandAsset.updateMany(
          { companyId: asset.companyId, type: asset.type || type, _id: { $ne: id } },
          { isPrimary: false }
        );
      }

      // Parse tags if sent as JSON string
      let parsedTags: string[] | undefined;
      if (tags) {
        try {
          parsedTags = typeof tags === 'string' ? JSON.parse(tags) : tags;
        } catch {
          parsedTags = undefined;
        }
      }

      const fileFormat = format || getExtensionFromMime(file.mimetype, 'png');

      Object.assign(asset, {
        name: name || asset.name,
        type: type || asset.type,
        description: description ?? asset.description,
        format: fileFormat,
        url: `/uploads/brand-assets/${file.filename}`,
        fileName: file.originalname,
        fileSize: file.size,
        fileType: file.mimetype,
        source: 'upload',
        sourceUrl: sourceUrl ?? asset.sourceUrl,
        tags: parsedTags ?? asset.tags,
        isPrimary: isPrimary === 'true' || isPrimary === true,
        ...(founderId !== undefined && { founderId }),
        ...(employeeId !== undefined && { employeeId }),
        updatedAt: new Date().toISOString(),
      });

      // Clear any legacy base64Data
      asset.base64Data = undefined;

      await asset.save();
      res.json(asset);
    } catch (error: any) {
      console.error('[BrandAssets] Upload update error:', error);
      if (error.name === 'ValidationError') {
        res.status(400).json({ error: error.message, details: Object.values(error.errors).map((e: any) => e.message) });
        return;
      }
      res.status(500).json({ error: 'Failed to update asset' });
    }
  }
);

// Update asset from JSON (metadata only — converts any base64Data to file)
router.put('/:id', requirePermission('brand-assets', 'edit'), async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { BrandAsset } = getModels();

    const asset = await BrandAsset.findById(id);
    if (!asset) {
      res.status(404).json({ error: 'Asset not found' });
      return;
    }

    if (!(req.user?.companyIds?.includes(asset.companyId)) && req.user?.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // If base64Data is being updated, convert it to a file
    if (req.body.base64Data) {
      try {
        const { buffer, mimeType } = base64ToBuffer(req.body.base64Data);
        const ext = getExtensionFromMime(mimeType, req.body.format || 'png');
        const { url: fileUrl, fileSize } = await saveBrandAssetFile(buffer, `asset.${ext}`, mimeType);

        // Delete the old file if it was stored locally
        if (asset.url && asset.url.startsWith('/uploads/brand-assets/')) {
          await deleteBrandAssetFile(asset.url);
          // Also clean up any cached format conversions
          deleteConvertedFiles(asset.url);
        }

        // Replace base64Data with file URL in the update
        req.body.url = fileUrl;
        req.body.fileSize = req.body.fileSize || fileSize;
        req.body.fileType = req.body.fileType || mimeType;
        req.body.source = req.body.source || 'upload';
        delete req.body.base64Data;
      } catch (err) {
        console.error('[BrandAssets] Failed to save base64 to file:', err);
        // Fall through — the base64Data will be stored in DB as before (legacy path)
      }
    }

    // If setting as primary, unset other primary assets of same type
    if (req.body.isPrimary && !asset.isPrimary) {
      await BrandAsset.updateMany(
        { companyId: asset.companyId, type: asset.type, _id: { $ne: id } },
        { isPrimary: false }
      );
    }

    Object.assign(asset, req.body, { updatedAt: new Date().toISOString() });
    await asset.save();

    res.json(asset);
  } catch (error: any) {
    console.error('[BrandAssets] Update error:', error);
    if (error.name === 'ValidationError') {
      res.status(400).json({ error: error.message, details: Object.values(error.errors).map((e: any) => e.message) });
      return;
    }
    res.status(500).json({ error: 'Failed to update asset' });
  }
});

// ─── DELETE ───────────────────────────────────────────────────────────────────

// Delete asset (also removes file from disk)
router.delete('/:id', requirePermission('brand-assets', 'delete'), async (req: Request, res: Response) => {
  try {
    const { id } = req.params;
    const { BrandAsset } = getModels();

    const asset = await BrandAsset.findById(id);
    if (!asset) {
      res.status(404).json({ error: 'Asset not found' });
      return;
    }

    if (!(req.user?.companyIds?.includes(asset.companyId)) && req.user?.role !== 'admin') {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // Delete the file from disk if it was stored locally
    if (asset.url && asset.url.startsWith('/uploads/brand-assets/')) {
      await deleteBrandAssetFile(asset.url);
      // Also clean up any cached format conversions
      deleteConvertedFiles(asset.url);
    }

    await BrandAsset.findByIdAndDelete(id);
    res.json({ message: 'Asset deleted successfully' });
  } catch (error) {
    console.error('[BrandAssets] Delete error:', error);
    res.status(500).json({ error: 'Failed to delete asset' });
  }
});

export default router;