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 { 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 logger from '@utils/logger';

const router = Router();
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[])
ensureUploadDir();
const storage = multer.diskStorage({
  destination: (_req, _file, cb) => cb(null, UPLOAD_DIR),
  filename: (_req, file, cb) => {
    const safeBase = path.basename(file.originalname).replace(/[^a-zA-Z0-9._-]/g, '_');
    const hash = crypto.randomBytes(6).toString('hex');
    cb(null, `${Date.now()}-${hash}-${safeBase}`);
  }
});
const upload = multer({ storage, limits: { fileSize: 10 * 1024 * 1024 } });

router.post('/multipart',
  authenticateToken,
  upload.array('files'),
  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,
  validateParams(workflowParamsSchema),
  upload.array('files'),
  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 storage/limits
router.post('/:id/work-notes',
  authenticateToken,
  validateParams(workflowParamsSchema),
  noteUpload.array('files'),
  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 document = await Document.findOne({ where: { documentId } });
    if (!document) {
      res.status(404).json({ success: false, error: 'Document not found' });
      return;
    }
    
    const filePath = (document as any).filePath;
    const fileName = (document as any).originalFileName || (document as any).fileName;
    const fileType = (document as any).fileType;
    
    // Check if file exists
    if (!require('fs').existsSync(filePath)) {
      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);
    
    // For images and PDFs, allow inline viewing
    const isPreviewable = 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(filePath, (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 document = await Document.findOne({ where: { documentId } });
    if (!document) {
      res.status(404).json({ success: false, error: 'Document not found' });
      return;
    }
    
    const filePath = (document as any).filePath;
    const fileName = (document as any).originalFileName || (document as any).fileName;
    
    // Check if file exists
    if (!require('fs').existsSync(filePath)) {
      res.status(404).json({ success: false, error: 'File not found on server' });
      return;
    }
    
    res.download(filePath, 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);
    
    // Check if file exists
    if (!require('fs').existsSync(fileInfo.filePath)) {
      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);
    
    // For images and PDFs, allow inline viewing
    const isPreviewable = 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(fileInfo.filePath, (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);
    
    // Check if file exists
    if (!require('fs').existsSync(fileInfo.filePath)) {
      res.status(404).json({ success: false, error: 'File not found' });
      return;
    }
    
    res.download(fileInfo.filePath, 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 
    });
  })
);

export default router;
