import { Router } from 'express';
import type { Request, Response } from 'express';
import { WorkflowController } from '../controllers/workflow.controller';
import { ApprovalController } from '../controllers/approval.controller';
import { authenticateToken } from '../middlewares/auth.middleware';
import { validateBody, validateParams } from '../middlewares/validate.middleware';
import { createWorkflowSchema, updateWorkflowSchema, workflowParamsSchema } from '../validators/workflow.validator';
import { approvalActionSchema, approvalParamsSchema } from '../validators/approval.validator';
import { asyncHandler } from '../middlewares/errorHandler.middleware';
import { requireParticipantTypes } from '../middlewares/authorization.middleware';
import multer from 'multer';
import path from 'path';
import crypto from 'crypto';
import { malwareScanMultipleMiddleware } from '../middlewares/malwareScan.middleware';
import { uploadLimiter } from '../middlewares/rateLimiter.middleware';
import { ensureUploadDir, UPLOAD_DIR } from '../config/storage';
import { notificationService } from '../services/notification.service';
import { Activity } from '@models/Activity';
import { WorkflowService } from '../services/workflow.service';
import { WorkNoteController } from '../controllers/worknote.controller';
import { workNoteService } from '../services/worknote.service';
import { pauseController } from '../controllers/pause.controller';
import logger from '@utils/logger';

const router = Router();

/**
 * Helper function to create proper Content-Disposition header
 * Returns clean filename header that browsers handle correctly
 */
function createContentDisposition(disposition: 'inline' | 'attachment', filename: string): string {
  // Clean filename: only remove truly problematic characters for HTTP headers
  // Keep spaces, dots, hyphens, underscores - these are safe
  const cleanFilename = filename
    .replace(/[<>:"|?*\x00-\x1F\x7F]/g, '_') // Only replace truly problematic chars
    .replace(/\\/g, '_') // Replace backslashes
    .trim();

  // For ASCII-only filenames, use simple format (browsers prefer this)
  // Only use filename* for non-ASCII characters
  const hasNonASCII = /[^\x00-\x7F]/.test(filename);

  if (hasNonASCII) {
    // Use RFC 5987 encoding for non-ASCII characters
    const encodedFilename = encodeURIComponent(filename);
    return `${disposition}; filename="${cleanFilename}"; filename*=UTF-8''${encodedFilename}`;
  } else {
    // Simple ASCII filename - use clean version (no filename* needed)
    // This prevents browsers from showing both filename and filename*
    return `${disposition}; filename="${cleanFilename}"`;
  }
}
const workflowController = new WorkflowController();
const approvalController = new ApprovalController();
const workNoteController = new WorkNoteController();

// Workflow routes
router.get('/',
  authenticateToken,
  asyncHandler(workflowController.listWorkflows.bind(workflowController))
);

// Filtered lists
// /my - All requests where user is a participant (not initiator) - for "All Requests" page (DEPRECATED - use /participant-requests)
router.get('/my',
  authenticateToken,
  asyncHandler(workflowController.listMyRequests.bind(workflowController))
);

// /participant-requests - All requests where user is a participant (not initiator) - for regular users' "All Requests" page
// SEPARATE endpoint from /workflows (admin) to avoid interference
router.get('/participant-requests',
  authenticateToken,
  asyncHandler(workflowController.listParticipantRequests.bind(workflowController))
);

// /my-initiated - Only requests where user is the initiator - for "My Requests" page
router.get('/my-initiated',
  authenticateToken,
  asyncHandler(workflowController.listMyInitiatedRequests.bind(workflowController))
);

router.get('/open-for-me',
  authenticateToken,
  asyncHandler(workflowController.listOpenForMe.bind(workflowController))
);

router.get('/closed-by-me',
  authenticateToken,
  asyncHandler(workflowController.listClosedByMe.bind(workflowController))
);

router.post('/',
  authenticateToken,
  validateBody(createWorkflowSchema),
  asyncHandler(workflowController.createWorkflow.bind(workflowController))
);

// Multipart create (payload + files[])
// Use memory storage for GCS uploads
const storage = multer.memoryStorage();
const upload = multer({ storage, limits: { fileSize: 10 * 1024 * 1024 } });

router.post('/multipart',
  authenticateToken,
  uploadLimiter,
  upload.array('files'),
  malwareScanMultipleMiddleware,
  asyncHandler(workflowController.createWorkflowMultipart.bind(workflowController))
);

router.get('/:id',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(workflowController.getWorkflow.bind(workflowController))
);

router.get('/:id/details',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(workflowController.getWorkflowDetails.bind(workflowController))
);

router.put('/:id',
  authenticateToken,
  validateParams(workflowParamsSchema),
  validateBody(updateWorkflowSchema),
  asyncHandler(workflowController.updateWorkflow.bind(workflowController))
);

// Multipart update (payload + files[]) for draft updates
router.put('/:id/multipart',
  authenticateToken,
  uploadLimiter,
  validateParams(workflowParamsSchema),
  upload.array('files'),
  malwareScanMultipleMiddleware,
  asyncHandler(workflowController.updateWorkflowMultipart.bind(workflowController))
);

router.patch('/:id/submit',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(workflowController.submitWorkflow.bind(workflowController))
);

// Approval routes
router.get('/:id/approvals',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(approvalController.getApprovalLevels.bind(approvalController))
);

router.get('/:id/approvals/current',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(approvalController.getCurrentApprovalLevel.bind(approvalController))
);

router.patch('/:id/approvals/:levelId/approve',
  authenticateToken,
  requireParticipantTypes(['APPROVER']),
  validateParams(approvalParamsSchema),
  validateBody(approvalActionSchema),
  asyncHandler(approvalController.approveLevel.bind(approvalController))
);

router.patch('/:id/approvals/:levelId/reject',
  authenticateToken,
  requireParticipantTypes(['APPROVER']),
  validateParams(approvalParamsSchema),
  validateBody(approvalActionSchema),
  asyncHandler(approvalController.approveLevel.bind(approvalController))
);

// Notifications
router.post('/notifications/subscribe',
  authenticateToken,
  asyncHandler(async (req: any, res: Response): Promise<void> => {
    const userId = req.user?.userId;
    if (!userId) { res.status(401).json({ success: false, error: 'Unauthorized' }); return; }
    const ua = req.headers['user-agent'] as string | undefined;
    await notificationService.addSubscription(userId, req.body, ua);
    res.json({ success: true });
    return;
  })
);

router.post('/notifications/test',
  authenticateToken,
  asyncHandler(async (req: any, res: Response): Promise<void> => {
    const userId = req.user?.userId;
    await notificationService.sendToUsers([userId], { title: 'Test', body: 'Push works!' });
    res.json({ success: true });
    return;
  })
);

// Activities
router.get('/:id/activity',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(async (req: any, res: Response): Promise<void> => {
    // Resolve requestId UUID from identifier
    const workflowService = new WorkflowService();
    const wf = await (workflowService as any).findWorkflowByIdentifier(req.params.id);
    if (!wf) { res.status(404).json({ success: false, error: 'Not found' }); return; }
    const requestId: string = wf.getDataValue('requestId');
    const rows = await Activity.findAll({ where: { requestId }, order: [['created_at', 'ASC']] as any });
    res.json({ success: true, data: rows });
    return;
  })
);

// Work Notes
router.get('/:id/work-notes',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(workNoteController.list.bind(workNoteController))
);

const noteUpload = upload; // reuse same memory storage/limits
router.post('/:id/work-notes',
  authenticateToken,
  uploadLimiter,
  validateParams(workflowParamsSchema),
  noteUpload.array('files'),
  malwareScanMultipleMiddleware,
  asyncHandler(workNoteController.create.bind(workNoteController))
);

// Preview workflow document
router.get('/documents/:documentId/preview',
  authenticateToken,
  asyncHandler(async (req: any, res: Response) => {
    const { documentId } = req.params;
    const { Document } = require('@models/Document');
    const { gcsStorageService } = require('../services/gcsStorage.service');
    const fs = require('fs');

    const document = await Document.findOne({ where: { documentId } });
    if (!document) {
      res.status(404).json({ success: false, error: 'Document not found' });
      return;
    }

    const storageUrl = (document as any).storageUrl || (document as any).storage_url;
    const filePath = (document as any).filePath || (document as any).file_path;
    const fileName = (document as any).originalFileName || (document as any).original_file_name || (document as any).fileName;
    const fileType = (document as any).mimeType || (document as any).mime_type;

    // Check if it's a GCS URL
    const isGcsUrl = storageUrl && (storageUrl.startsWith('https://storage.googleapis.com') || storageUrl.startsWith('gs://'));

    if (isGcsUrl) {
      // Redirect to GCS public URL or use signed URL for private files
      res.redirect(storageUrl);
      return;
    }

    // If storageUrl is null but filePath indicates GCS storage, stream file directly from GCS
    if (!storageUrl && filePath && filePath.startsWith('requests/')) {
      try {
        // Use the existing GCS storage service instance
        if (!gcsStorageService.isConfigured()) {
          throw new Error('GCS not configured');
        }

        // Access the storage instance from the service
        const { Storage } = require('@google-cloud/storage');
        const keyFilePath = process.env.GCP_KEY_FILE || '';
        const bucketName = process.env.GCP_BUCKET_NAME || '';
        const path = require('path');
        const resolvedKeyPath = path.isAbsolute(keyFilePath)
          ? keyFilePath
          : path.resolve(process.cwd(), keyFilePath);

        const storage = new Storage({
          projectId: process.env.GCP_PROJECT_ID || '',
          keyFilename: resolvedKeyPath,
        });

        const bucket = storage.bucket(bucketName);
        const file = bucket.file(filePath);

        // Check if file exists
        const [exists] = await file.exists();
        if (!exists) {
          res.status(404).json({ success: false, error: 'File not found in GCS' });
          return;
        }

        // Get file metadata for content type
        const [metadata] = await file.getMetadata();
        const contentType = metadata.contentType || fileType || 'application/octet-stream';

        // Set CORS headers
        const origin = req.headers.origin;
        if (origin) {
          res.setHeader('Access-Control-Allow-Origin', origin);
          res.setHeader('Access-Control-Allow-Credentials', 'true');
        }
        res.setHeader('Access-Control-Expose-Headers', 'Content-Type, Content-Disposition');
        res.setHeader('Content-Type', contentType);

        // For images and PDFs, allow inline viewing
        const isPreviewable = fileType && (fileType.includes('image') || fileType.includes('pdf'));
        const disposition = isPreviewable ? 'inline' : 'attachment';
        res.setHeader('Content-Disposition', createContentDisposition(disposition, fileName));

        // Stream file from GCS to response
        file.createReadStream()
          .on('error', (streamError: Error) => {
            const logger = require('../utils/logger').default;
            logger.error('[Workflow] Failed to stream file from GCS', {
              documentId,
              filePath,
              error: streamError.message,
            });
            if (!res.headersSent) {
              res.status(500).json({
                success: false,
                error: 'Failed to stream file from storage'
              });
            }
          })
          .pipe(res);
        return;
      } catch (gcsError) {
        const logger = require('../utils/logger').default;
        logger.error('[Workflow] Failed to access GCS file for preview', {
          documentId,
          filePath,
          error: gcsError instanceof Error ? gcsError.message : 'Unknown error',
        });
        res.status(500).json({
          success: false,
          error: 'Failed to access file. Please try again.'
        });
        return;
      }
    }

    // Local file handling - check if storageUrl is a local path (starts with /uploads/)
    if (storageUrl && storageUrl.startsWith('/uploads/')) {
      // Extract relative path from storageUrl (remove /uploads/ prefix)
      const relativePath = storageUrl.replace(/^\/uploads\//, '');
      const absolutePath = path.join(UPLOAD_DIR, relativePath);

      // Check if file exists
      if (!fs.existsSync(absolutePath)) {
        res.status(404).json({ success: false, error: 'File not found on server' });
        return;
      }

      // Set CORS headers to allow blob URL creation when served from same origin
      const origin = req.headers.origin;
      if (origin) {
        res.setHeader('Access-Control-Allow-Origin', origin);
        res.setHeader('Access-Control-Allow-Credentials', 'true');
      }
      res.setHeader('Access-Control-Expose-Headers', 'Content-Type, Content-Disposition');

      // Set appropriate content type
      res.contentType(fileType || 'application/octet-stream');

      // For images and PDFs, allow inline viewing
      const isPreviewable = fileType && (fileType.includes('image') || fileType.includes('pdf'));
      if (isPreviewable) {
        res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
      } else {
        res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
      }

      res.sendFile(absolutePath, (err) => {
        if (err && !res.headersSent) {
          res.status(500).json({ success: false, error: 'Failed to serve file' });
        }
      });
      return;
    }

    // Legacy local file handling (absolute path stored in filePath)
    // Resolve relative path if needed
    const absolutePath = filePath && !path.isAbsolute(filePath)
      ? path.join(UPLOAD_DIR, filePath)
      : filePath;

    if (!absolutePath || !fs.existsSync(absolutePath)) {
      res.status(404).json({ success: false, error: 'File not found on server' });
      return;
    }

    // Set CORS headers to allow blob URL creation when served from same origin
    const origin = req.headers.origin;
    if (origin) {
      res.setHeader('Access-Control-Allow-Origin', origin);
      res.setHeader('Access-Control-Allow-Credentials', 'true');
    }
    res.setHeader('Access-Control-Expose-Headers', 'Content-Type, Content-Disposition');

    // Set appropriate content type
    res.contentType(fileType || 'application/octet-stream');

    // For images and PDFs, allow inline viewing
    const isPreviewable = fileType && (fileType.includes('image') || fileType.includes('pdf'));
    if (isPreviewable) {
      res.setHeader('Content-Disposition', `inline; filename="${fileName}"`);
    } else {
      res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
    }

    res.sendFile(absolutePath, (err) => {
      if (err && !res.headersSent) {
        res.status(500).json({ success: false, error: 'Failed to serve file' });
      }
    });
  })
);

// Download workflow document
router.get('/documents/:documentId/download',
  authenticateToken,
  asyncHandler(async (req: any, res: Response) => {
    const { documentId } = req.params;
    const { Document } = require('@models/Document');
    const { gcsStorageService } = require('../services/gcsStorage.service');
    const fs = require('fs');

    const document = await Document.findOne({ where: { documentId } });
    if (!document) {
      res.status(404).json({ success: false, error: 'Document not found' });
      return;
    }

    const storageUrl = (document as any).storageUrl || (document as any).storage_url;
    const filePath = (document as any).filePath || (document as any).file_path;
    const fileName = (document as any).originalFileName || (document as any).original_file_name || (document as any).fileName;

    // Check if it's a GCS URL
    const isGcsUrl = storageUrl && (storageUrl.startsWith('https://storage.googleapis.com') || storageUrl.startsWith('gs://'));

    if (isGcsUrl) {
      // Redirect to GCS public URL for download
      res.redirect(storageUrl);
      return;
    }

    // If storageUrl is null but filePath indicates GCS storage, stream file directly from GCS
    if (!storageUrl && filePath && filePath.startsWith('requests/')) {
      try {
        // Use the existing GCS storage service instance
        if (!gcsStorageService.isConfigured()) {
          throw new Error('GCS not configured');
        }

        // Access the storage instance from the service
        const { Storage } = require('@google-cloud/storage');
        const keyFilePath = process.env.GCP_KEY_FILE || '';
        const bucketName = process.env.GCP_BUCKET_NAME || '';
        const path = require('path');
        const resolvedKeyPath = path.isAbsolute(keyFilePath)
          ? keyFilePath
          : path.resolve(process.cwd(), keyFilePath);

        const storage = new Storage({
          projectId: process.env.GCP_PROJECT_ID || '',
          keyFilename: resolvedKeyPath,
        });

        const bucket = storage.bucket(bucketName);
        const file = bucket.file(filePath);

        // Check if file exists
        const [exists] = await file.exists();
        if (!exists) {
          res.status(404).json({ success: false, error: 'File not found in GCS' });
          return;
        }

        // Get file metadata for content type
        const [metadata] = await file.getMetadata();
        const contentType = metadata.contentType || (document as any).mimeType || (document as any).mime_type || 'application/octet-stream';

        // Set CORS headers
        const origin = req.headers.origin;
        if (origin) {
          res.setHeader('Access-Control-Allow-Origin', origin);
          res.setHeader('Access-Control-Allow-Credentials', 'true');
        }
        res.setHeader('Access-Control-Expose-Headers', 'Content-Type, Content-Disposition');

        // Set headers for download
        res.setHeader('Content-Type', contentType);
        res.setHeader('Content-Disposition', createContentDisposition('attachment', fileName));

        // Stream file from GCS to response
        file.createReadStream()
          .on('error', (streamError: Error) => {
            const logger = require('../utils/logger').default;
            logger.error('[Workflow] Failed to stream file from GCS for download', {
              documentId,
              filePath,
              error: streamError.message,
            });
            if (!res.headersSent) {
              res.status(500).json({
                success: false,
                error: 'Failed to stream file from storage'
              });
            }
          })
          .pipe(res);
        return;
      } catch (gcsError) {
        const logger = require('../utils/logger').default;
        logger.error('[Workflow] Failed to access GCS file for download', {
          documentId,
          filePath,
          error: gcsError instanceof Error ? gcsError.message : 'Unknown error',
        });
        res.status(500).json({
          success: false,
          error: 'Failed to access file. Please try again.'
        });
        return;
      }
    }

    // Local file handling - check if storageUrl is a local path (starts with /uploads/)
    if (storageUrl && storageUrl.startsWith('/uploads/')) {
      // Extract relative path from storageUrl (remove /uploads/ prefix)
      const relativePath = storageUrl.replace(/^\/uploads\//, '');
      const absolutePath = path.join(UPLOAD_DIR, relativePath);

      // Check if file exists
      if (!fs.existsSync(absolutePath)) {
        res.status(404).json({ success: false, error: 'File not found on server' });
        return;
      }

      // Set CORS headers
      const origin = req.headers.origin;
      if (origin) {
        res.setHeader('Access-Control-Allow-Origin', origin);
        res.setHeader('Access-Control-Allow-Credentials', 'true');
      }
      res.setHeader('Access-Control-Expose-Headers', 'Content-Type, Content-Disposition');

      // Set headers for download
      const fileTypeForDownload = (document as any).mimeType || (document as any).mime_type || 'application/octet-stream';
      res.setHeader('Content-Type', fileTypeForDownload);
      res.setHeader('Content-Disposition', createContentDisposition('attachment', fileName));

      res.download(absolutePath, fileName, (err) => {
        if (err && !res.headersSent) {
          res.status(500).json({ success: false, error: 'Failed to download file' });
        }
      });
      return;
    }

    // Legacy local file handling (absolute path stored in filePath)
    // Resolve relative path if needed
    const absolutePath = filePath && !path.isAbsolute(filePath)
      ? path.join(UPLOAD_DIR, filePath)
      : filePath;

    if (!absolutePath || !fs.existsSync(absolutePath)) {
      res.status(404).json({ success: false, error: 'File not found on server' });
      return;
    }

    res.download(absolutePath, fileName, (err) => {
      if (err && !res.headersSent) {
        res.status(500).json({ success: false, error: 'Failed to download file' });
      }
    });
  })
);

// Preview work note attachment (serves file for inline viewing)
router.get('/work-notes/attachments/:attachmentId/preview',
  authenticateToken,
  asyncHandler(async (req: any, res: Response) => {
    const { attachmentId } = req.params;
    const fileInfo = await workNoteService.downloadAttachment(attachmentId);
    const fs = require('fs');

    // Check if it's a GCS URL
    if (fileInfo.isGcsUrl && fileInfo.storageUrl) {
      // Redirect to GCS public URL
      res.redirect(fileInfo.storageUrl);
      return;
    }

    // Local file handling - check if storageUrl is a local path (starts with /uploads/)
    if (fileInfo.storageUrl && fileInfo.storageUrl.startsWith('/uploads/')) {
      // Extract relative path from storageUrl (remove /uploads/ prefix)
      const relativePath = fileInfo.storageUrl.replace(/^\/uploads\//, '');
      const absolutePath = path.join(UPLOAD_DIR, relativePath);

      // Check if file exists
      if (!fs.existsSync(absolutePath)) {
        res.status(404).json({ success: false, error: 'File not found' });
        return;
      }

      // Set CORS headers to allow blob URL creation when served from same origin
      const origin = req.headers.origin;
      if (origin) {
        res.setHeader('Access-Control-Allow-Origin', origin);
        res.setHeader('Access-Control-Allow-Credentials', 'true');
      }
      res.setHeader('Access-Control-Expose-Headers', 'Content-Type, Content-Disposition');

      // Set appropriate content type
      res.contentType(fileInfo.fileType || 'application/octet-stream');

      // For images and PDFs, allow inline viewing
      const isPreviewable = fileInfo.fileType && (fileInfo.fileType.includes('image') || fileInfo.fileType.includes('pdf'));
      if (isPreviewable) {
        res.setHeader('Content-Disposition', `inline; filename="${fileInfo.fileName}"`);
      } else {
        res.setHeader('Content-Disposition', `attachment; filename="${fileInfo.fileName}"`);
      }

      res.sendFile(absolutePath, (err) => {
        if (err && !res.headersSent) {
          res.status(500).json({ success: false, error: 'Failed to serve file' });
        }
      });
      return;
    }

    // Legacy local file handling (absolute path stored in filePath)
    // Resolve relative path if needed
    const absolutePath = fileInfo.filePath && !path.isAbsolute(fileInfo.filePath)
      ? path.join(UPLOAD_DIR, fileInfo.filePath)
      : fileInfo.filePath;

    if (!absolutePath || !fs.existsSync(absolutePath)) {
      res.status(404).json({ success: false, error: 'File not found' });
      return;
    }

    // Set CORS headers to allow blob URL creation when served from same origin
    const origin = req.headers.origin;
    if (origin) {
      res.setHeader('Access-Control-Allow-Origin', origin);
      res.setHeader('Access-Control-Allow-Credentials', 'true');
    }
    res.setHeader('Access-Control-Expose-Headers', 'Content-Type, Content-Disposition');

    // Set appropriate content type
    res.contentType(fileInfo.fileType || 'application/octet-stream');

    // For images and PDFs, allow inline viewing
    const isPreviewable = fileInfo.fileType && (fileInfo.fileType.includes('image') || fileInfo.fileType.includes('pdf'));
    if (isPreviewable) {
      res.setHeader('Content-Disposition', `inline; filename="${fileInfo.fileName}"`);
    } else {
      res.setHeader('Content-Disposition', `attachment; filename="${fileInfo.fileName}"`);
    }

    res.sendFile(absolutePath, (err) => {
      if (err && !res.headersSent) {
        res.status(500).json({ success: false, error: 'Failed to serve file' });
      }
    });
  })
);

// Download work note attachment
router.get('/work-notes/attachments/:attachmentId/download',
  authenticateToken,
  asyncHandler(async (req: any, res: Response) => {
    const { attachmentId } = req.params;
    const fileInfo = await workNoteService.downloadAttachment(attachmentId);
    const fs = require('fs');

    // Check if it's a GCS URL
    if (fileInfo.isGcsUrl && fileInfo.storageUrl) {
      // Redirect to GCS public URL for download
      res.redirect(fileInfo.storageUrl);
      return;
    }

    // Local file handling - check if storageUrl is a local path (starts with /uploads/)
    if (fileInfo.storageUrl && fileInfo.storageUrl.startsWith('/uploads/')) {
      // Extract relative path from storageUrl (remove /uploads/ prefix)
      const relativePath = fileInfo.storageUrl.replace(/^\/uploads\//, '');
      const absolutePath = path.join(UPLOAD_DIR, relativePath);

      // Check if file exists
      if (!fs.existsSync(absolutePath)) {
        res.status(404).json({ success: false, error: 'File not found' });
        return;
      }

      // Set CORS headers
      const origin = req.headers.origin;
      if (origin) {
        res.setHeader('Access-Control-Allow-Origin', origin);
        res.setHeader('Access-Control-Allow-Credentials', 'true');
      }
      res.setHeader('Access-Control-Expose-Headers', 'Content-Type, Content-Disposition');

      res.download(absolutePath, fileInfo.fileName, (err) => {
        if (err && !res.headersSent) {
          res.status(500).json({ success: false, error: 'Failed to download file' });
        }
      });
      return;
    }

    // Legacy local file handling (absolute path stored in filePath)
    // Resolve relative path if needed
    const absolutePath = fileInfo.filePath && !path.isAbsolute(fileInfo.filePath)
      ? path.join(UPLOAD_DIR, fileInfo.filePath)
      : fileInfo.filePath;

    if (!absolutePath || !fs.existsSync(absolutePath)) {
      res.status(404).json({ success: false, error: 'File not found' });
      return;
    }

    res.download(absolutePath, fileInfo.fileName, (err) => {
      if (err && !res.headersSent) {
        res.status(500).json({ success: false, error: 'Failed to download file' });
      }
    });
  })
);

// Add participant routes
router.post('/:id/participants/approver',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(async (req: any, res: Response) => {
    const workflowService = new WorkflowService();
    const wf = await (workflowService as any).findWorkflowByIdentifier(req.params.id);
    if (!wf) {
      res.status(404).json({ success: false, error: 'Workflow not found' });
      return;
    }
    const requestId: string = wf.getDataValue('requestId');
    const { email } = req.body;

    if (!email) {
      res.status(400).json({ success: false, error: 'Email is required' });
      return;
    }

    const participant = await workflowService.addApprover(requestId, email, req.user?.userId);
    res.status(201).json({ success: true, data: participant });
  })
);

router.post('/:id/participants/spectator',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(async (req: any, res: Response) => {
    const workflowService = new WorkflowService();
    const wf = await (workflowService as any).findWorkflowByIdentifier(req.params.id);
    if (!wf) {
      res.status(404).json({ success: false, error: 'Workflow not found' });
      return;
    }
    const requestId: string = wf.getDataValue('requestId');
    const { email } = req.body;

    if (!email) {
      res.status(400).json({ success: false, error: 'Email is required' });
      return;
    }

    const participant = await workflowService.addSpectator(requestId, email, req.user?.userId);
    res.status(201).json({ success: true, data: participant });
  })
);

// Skip approver endpoint
router.post('/:id/approvals/:levelId/skip',
  authenticateToken,
  requireParticipantTypes(['INITIATOR', 'APPROVER']),  // Only initiator or other approvers can skip
  validateParams(approvalParamsSchema),
  asyncHandler(async (req: any, res: Response) => {
    const workflowService = new WorkflowService();
    const wf = await (workflowService as any).findWorkflowByIdentifier(req.params.id);
    if (!wf) {
      res.status(404).json({ success: false, error: 'Workflow not found' });
      return;
    }
    const requestId: string = wf.getDataValue('requestId');
    const { levelId } = req.params;
    const { reason } = req.body;

    const result = await workflowService.skipApprover(
      requestId,
      levelId,
      reason || '',
      req.user?.userId
    );

    res.status(200).json({
      success: true,
      message: 'Approver skipped successfully',
      data: result
    });
  })
);

// Add approver at specific level with level shifting
router.post('/:id/approvers/at-level',
  authenticateToken,
  requireParticipantTypes(['INITIATOR', 'APPROVER']),  // Only initiator or approvers can add new approvers
  validateParams(workflowParamsSchema),
  asyncHandler(async (req: any, res: Response) => {
    const workflowService = new WorkflowService();
    const wf = await (workflowService as any).findWorkflowByIdentifier(req.params.id);
    if (!wf) {
      res.status(404).json({ success: false, error: 'Workflow not found' });
      return;
    }
    const requestId: string = wf.getDataValue('requestId');
    const { email, tatHours, level } = req.body;

    if (!email || !tatHours || !level) {
      res.status(400).json({
        success: false,
        error: 'Email, tatHours, and level are required'
      });
      return;
    }

    const result = await workflowService.addApproverAtLevel(
      requestId,
      email,
      Number(tatHours),
      Number(level),
      req.user?.userId
    );

    res.status(201).json({
      success: true,
      message: 'Approver added successfully',
      data: result
    });
  })
);

// Pause workflow routes
// POST /workflows/:id/pause - Pause a workflow (approver or initiator)
router.post('/:id/pause',
  authenticateToken,
  requireParticipantTypes(['APPROVER', 'INITIATOR']), // Both approvers and initiators can pause
  validateParams(workflowParamsSchema),
  asyncHandler(pauseController.pauseWorkflow.bind(pauseController))
);

// POST /workflows/:id/resume - Resume a paused workflow (approver or initiator)
router.post('/:id/resume',
  authenticateToken,
  requireParticipantTypes(['APPROVER', 'INITIATOR']), // Both approvers and initiators can resume
  validateParams(workflowParamsSchema),
  asyncHandler(pauseController.resumeWorkflow.bind(pauseController))
);

// POST /workflows/:id/pause/retrigger - Retrigger pause (initiator requests approver to resume)
router.post('/:id/pause/retrigger',
  authenticateToken,
  requireParticipantTypes(['INITIATOR']), // Only initiator can retrigger
  validateParams(workflowParamsSchema),
  asyncHandler(pauseController.retriggerPause.bind(pauseController))
);

// GET /workflows/:id/pause - Get pause details
router.get('/:id/pause',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(pauseController.getPauseDetails.bind(pauseController))
);

// Initiator actions for rejected/returned requests
router.post('/:id/initiator-action',
  authenticateToken,
  requireParticipantTypes(['INITIATOR']),
  validateParams(workflowParamsSchema),
  asyncHandler(workflowController.handleInitiatorAction.bind(workflowController))
);

// Get revision history
router.get('/:id/history',
  authenticateToken,
  validateParams(workflowParamsSchema),
  asyncHandler(workflowController.getHistory.bind(workflowController))
);

export default router;
