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 { documentController } from '../controllers/document.controller';
import { pauseController } from '../controllers/pause.controller';

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((req: any, res: Response) => documentController.getWorkflowDocument(req, res, 'preview'))
);

// Download workflow document
router.get('/documents/:documentId/download',
  authenticateToken,
  asyncHandler((req: any, res: Response) => documentController.getWorkflowDocument(req, res, 'download'))
);

// Preview work note attachment
router.get('/work-notes/attachments/:attachmentId/preview',
  authenticateToken,
  asyncHandler((req: any, res: Response) => documentController.getWorkNoteAttachment(req, res, 'preview'))
);

// Download work note attachment
router.get('/work-notes/attachments/:attachmentId/download',
  authenticateToken,
  asyncHandler((req: any, res: Response) => documentController.getWorkNoteAttachment(req, res, 'download'))
);

// 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;
