import { Request, Response } from 'express';
import { WorkflowService } from '@services/workflow.service';
import { validateCreateWorkflow, validateUpdateWorkflow } from '@validators/workflow.validator';
import { ResponseHandler } from '@utils/responseHandler';
import type { AuthenticatedRequest } from '../types/express';
import { Priority } from '../types/common.types';
import type { UpdateWorkflowRequest } from '../types/workflow.types';
import { Document } from '@models/Document';
import { User } from '@models/User';
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import { getRequestMetadata } from '@utils/requestUtils';

const workflowService = new WorkflowService();

export class WorkflowController {
  async createWorkflow(req: AuthenticatedRequest, res: Response): Promise<void> {
    try {
      const validatedData = validateCreateWorkflow(req.body);
      // Convert string literal priority to enum
      const workflowData = {
        ...validatedData,
        priority: validatedData.priority as Priority
      };
      const requestMeta = getRequestMetadata(req);
      const workflow = await workflowService.createWorkflow(req.user.userId, workflowData, {
        ipAddress: requestMeta.ipAddress,
        userAgent: requestMeta.userAgent
      });
      
      ResponseHandler.success(res, workflow, 'Workflow created successfully', 201);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to create workflow', 400, errorMessage);
    }
  }

  // Multipart create: accepts payload JSON and files[]
  async createWorkflowMultipart(req: AuthenticatedRequest, res: Response): Promise<void> {
    try {
      const userId = req.user?.userId;
      if (!userId) {
        ResponseHandler.error(res, 'Unauthorized', 401);
        return;
      }

      const raw = String(req.body?.payload || '');
      if (!raw) {
        ResponseHandler.error(res, 'payload is required', 400);
        return;
      }
      
      let parsed;
      try {
        parsed = JSON.parse(raw);
      } catch (parseError) {
        ResponseHandler.error(res, 'Invalid JSON in payload', 400, parseError instanceof Error ? parseError.message : 'JSON parse error');
        return;
      }
      
      let validated;
      try {
        validated = validateCreateWorkflow(parsed);
      } catch (validationError: any) {
        // Zod validation errors provide detailed information
        const errorMessage = validationError?.errors 
          ? validationError.errors.map((e: any) => `${e.path.join('.')}: ${e.message}`).join('; ')
          : (validationError instanceof Error ? validationError.message : 'Validation failed');
        ResponseHandler.error(res, 'Validation failed', 400, errorMessage);
        return;
      }
      
      const workflowData = { ...validated, priority: validated.priority as Priority } as any;

      const requestMeta = getRequestMetadata(req);
      const workflow = await workflowService.createWorkflow(userId, workflowData, {
        ipAddress: requestMeta.ipAddress,
        userAgent: requestMeta.userAgent
      });

      // Attach files as documents (category defaults to SUPPORTING)
      const files = (req as any).files as Express.Multer.File[] | undefined;
      const category = (req.body?.category as string) || 'OTHER';
      const docs: any[] = [];
      if (files && files.length > 0) {
        const { activityService } = require('../services/activity.service');
        const user = await User.findByPk(userId);
        const uploaderName = (user as any)?.displayName || (user as any)?.email || 'User';
        
        for (const file of files) {
          const buffer = fs.readFileSync(file.path);
          const checksum = crypto.createHash('sha256').update(buffer).digest('hex');
          const extension = path.extname(file.originalname).replace('.', '').toLowerCase();
          const doc = await Document.create({
            requestId: workflow.requestId,
            uploadedBy: userId,
            fileName: path.basename(file.filename || file.originalname),
            originalFileName: file.originalname,
            fileType: extension,
            fileExtension: extension,
            fileSize: file.size,
            filePath: file.path,
            storageUrl: `/uploads/${path.basename(file.path)}`,
            mimeType: file.mimetype,
            checksum,
            isGoogleDoc: false,
            googleDocUrl: null as any,
            category: category || 'OTHER',
            version: 1,
            parentDocumentId: null as any,
            isDeleted: false,
            downloadCount: 0,
          } as any);
          docs.push(doc);
          
          // Log document upload activity
          const requestMeta = getRequestMetadata(req);
          activityService.log({
            requestId: workflow.requestId,
            type: 'document_added',
            user: { userId, name: uploaderName },
            timestamp: new Date().toISOString(),
            action: 'Document Added',
            details: `Added ${file.originalname} as supporting document by ${uploaderName}`,
            metadata: { fileName: file.originalname, fileSize: file.size, fileType: extension },
            ipAddress: requestMeta.ipAddress,
            userAgent: requestMeta.userAgent
          });
        }
      }

      ResponseHandler.success(res, { requestId: workflow.requestId, documents: docs }, 'Workflow created with documents', 201);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to create workflow', 400, errorMessage);
    }
  }

  async getWorkflow(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params;
      const workflow = await workflowService.getWorkflowById(id);
      
      if (!workflow) {
        ResponseHandler.notFound(res, 'Workflow not found');
        return;
      }

      ResponseHandler.success(res, workflow, 'Workflow retrieved successfully');
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to get workflow', 500, errorMessage);
    }
  }

  async getWorkflowDetails(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params as any;
      const result = await workflowService.getWorkflowDetails(id);
      if (!result) {
        ResponseHandler.notFound(res, 'Workflow not found');
        return;
      }
      ResponseHandler.success(res, result, 'Workflow details fetched');
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to fetch workflow details', 500, errorMessage);
    }
  }

  async listWorkflows(req: Request, res: Response): Promise<void> {
    try {
      const page = Math.max(parseInt(String(req.query.page || '1'), 10), 1);
      const limit = Math.min(Math.max(parseInt(String(req.query.limit || '20'), 10), 1), 100);
      
      // Extract filter parameters
      const filters = {
        search: req.query.search as string | undefined,
        status: req.query.status as string | undefined,
        priority: req.query.priority as string | undefined,
        department: req.query.department as string | undefined,
        initiator: req.query.initiator as string | undefined,
        approver: req.query.approver as string | undefined,
        approverType: req.query.approverType as 'current' | 'any' | undefined,
        slaCompliance: req.query.slaCompliance as string | undefined,
        dateRange: req.query.dateRange as string | undefined,
        startDate: req.query.startDate as string | undefined,
        endDate: req.query.endDate as string | undefined,
      };
      
      const result = await workflowService.listWorkflows(page, limit, filters);
      ResponseHandler.success(res, result, 'Workflows fetched');
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to list workflows', 500, errorMessage);
    }
  }

  async listMyRequests(req: Request, res: Response): Promise<void> {
    try {
      const userId = (req as any).user?.userId || (req as any).user?.id || (req as any).auth?.userId;
      const page = Math.max(parseInt(String(req.query.page || '1'), 10), 1);
      const limit = Math.min(Math.max(parseInt(String(req.query.limit || '20'), 10), 1), 100);
      
      // Extract filter parameters (same as listWorkflows)
      const search = req.query.search as string | undefined;
      const status = req.query.status as string | undefined;
      const priority = req.query.priority as string | undefined;
      const department = req.query.department as string | undefined;
      const initiator = req.query.initiator as string | undefined;
      const approver = req.query.approver as string | undefined;
      const approverType = req.query.approverType as 'current' | 'any' | undefined;
      const slaCompliance = req.query.slaCompliance as string | undefined;
      const dateRange = req.query.dateRange as string | undefined;
      const startDate = req.query.startDate as string | undefined;
      const endDate = req.query.endDate as string | undefined;
      
      const filters = { search, status, priority, department, initiator, approver, approverType, slaCompliance, dateRange, startDate, endDate };
      
      const result = await workflowService.listMyRequests(userId, page, limit, filters);
      ResponseHandler.success(res, result, 'My requests fetched');
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to fetch my requests', 500, errorMessage);
    }
  }

  /**
   * List requests where user is a PARTICIPANT (not initiator) - for regular users' "All Requests" page
   * Completely separate from listWorkflows (admin) to avoid interference
   */
  async listParticipantRequests(req: Request, res: Response): Promise<void> {
    try {
      const userId = (req as any).user?.userId || (req as any).user?.id || (req as any).auth?.userId;
      const page = Math.max(parseInt(String(req.query.page || '1'), 10), 1);
      const limit = Math.min(Math.max(parseInt(String(req.query.limit || '20'), 10), 1), 100);
      
      // Extract filter parameters (same as listWorkflows)
      const search = req.query.search as string | undefined;
      const status = req.query.status as string | undefined;
      const priority = req.query.priority as string | undefined;
      const department = req.query.department as string | undefined;
      const initiator = req.query.initiator as string | undefined;
      const approver = req.query.approver as string | undefined;
      const approverType = req.query.approverType as 'current' | 'any' | undefined;
      const slaCompliance = req.query.slaCompliance as string | undefined;
      const dateRange = req.query.dateRange as string | undefined;
      const startDate = req.query.startDate as string | undefined;
      const endDate = req.query.endDate as string | undefined;
      
      const filters = { search, status, priority, department, initiator, approver, approverType, slaCompliance, dateRange, startDate, endDate };
      
      const result = await workflowService.listParticipantRequests(userId, page, limit, filters);
      ResponseHandler.success(res, result, 'Participant requests fetched');
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to fetch participant requests', 500, errorMessage);
    }
  }

  /**
   * List requests where user is the initiator (for "My Requests" page)
   */
  async listMyInitiatedRequests(req: Request, res: Response): Promise<void> {
    try {
      const userId = (req as any).user?.userId || (req as any).user?.id || (req as any).auth?.userId;
      const page = Math.max(parseInt(String(req.query.page || '1'), 10), 1);
      const limit = Math.min(Math.max(parseInt(String(req.query.limit || '20'), 10), 1), 100);
      
      // Extract filter parameters
      const search = req.query.search as string | undefined;
      const status = req.query.status as string | undefined;
      const priority = req.query.priority as string | undefined;
      const department = req.query.department as string | undefined;
      const slaCompliance = req.query.slaCompliance as string | undefined;
      const dateRange = req.query.dateRange as string | undefined;
      const startDate = req.query.startDate as string | undefined;
      const endDate = req.query.endDate as string | undefined;
      
      const filters = { search, status, priority, department, slaCompliance, dateRange, startDate, endDate };
      
      const result = await workflowService.listMyInitiatedRequests(userId, page, limit, filters);
      ResponseHandler.success(res, result, 'My initiated requests fetched');
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to fetch my initiated requests', 500, errorMessage);
    }
  }

  async listOpenForMe(req: Request, res: Response): Promise<void> {
    try {
      const userId = (req as any).user?.userId || (req as any).user?.id || (req as any).auth?.userId;
      const page = Math.max(parseInt(String(req.query.page || '1'), 10), 1);
      const limit = Math.min(Math.max(parseInt(String(req.query.limit || '20'), 10), 1), 100);
      
      // Extract filter parameters
      const filters = {
        search: req.query.search as string | undefined,
        status: req.query.status as string | undefined,
        priority: req.query.priority as string | undefined
      };
      
      // Extract sorting parameters
      const sortBy = req.query.sortBy as string | undefined;
      const sortOrder = (req.query.sortOrder as string | undefined) || 'desc';
      
      const result = await workflowService.listOpenForMe(userId, page, limit, filters, sortBy, sortOrder);
      ResponseHandler.success(res, result, 'Open requests for user fetched');
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to fetch open requests for user', 500, errorMessage);
    }
  }

  async listClosedByMe(req: Request, res: Response): Promise<void> {
    try {
      const userId = (req as any).user?.userId || (req as any).user?.id || (req as any).auth?.userId;
      const page = Math.max(parseInt(String(req.query.page || '1'), 10), 1);
      const limit = Math.min(Math.max(parseInt(String(req.query.limit || '20'), 10), 1), 100);
      
      // Extract filter parameters
      const filters = {
        search: req.query.search as string | undefined,
        status: req.query.status as string | undefined,
        priority: req.query.priority as string | undefined
      };
      
      // Extract sorting parameters
      const sortBy = req.query.sortBy as string | undefined;
      const sortOrder = (req.query.sortOrder as string | undefined) || 'desc';
      
      const result = await workflowService.listClosedByMe(userId, page, limit, filters, sortBy, sortOrder);
      ResponseHandler.success(res, result, 'Closed requests by user fetched');
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to fetch closed requests by user', 500, errorMessage);
    }
  }

  async updateWorkflow(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params;
      const validatedData = validateUpdateWorkflow(req.body);
      // Build a strongly-typed payload for the service layer
      const updateData: UpdateWorkflowRequest = { ...validatedData } as any;
      if (validatedData.priority) {
        // Map string literal to enum value explicitly
        updateData.priority = validatedData.priority === 'EXPRESS' ? Priority.EXPRESS : Priority.STANDARD;
      }
      
      const workflow = await workflowService.updateWorkflow(id, updateData);
      
      if (!workflow) {
        ResponseHandler.notFound(res, 'Workflow not found');
        return;
      }

      ResponseHandler.success(res, workflow, 'Workflow updated successfully');
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to update workflow', 400, errorMessage);
    }
  }

  // Multipart update for drafts: accepts payload JSON and files[]
  async updateWorkflowMultipart(req: AuthenticatedRequest, res: Response): Promise<void> {
    try {
      const userId = req.user?.userId;
      if (!userId) {
        ResponseHandler.error(res, 'Unauthorized', 401);
        return;
      }

      const { id } = req.params;
      const raw = String(req.body?.payload || '');
      if (!raw) {
        ResponseHandler.error(res, 'payload is required', 400);
        return;
      }
      const parsed = JSON.parse(raw);
      const validated = validateUpdateWorkflow(parsed);
      const updateData: UpdateWorkflowRequest = { ...validated } as any;
      if (validated.priority) {
        updateData.priority = validated.priority === 'EXPRESS' ? Priority.EXPRESS : Priority.STANDARD;
      }

      // Update workflow
      const workflow = await workflowService.updateWorkflow(id, updateData);
      if (!workflow) {
        ResponseHandler.notFound(res, 'Workflow not found');
        return;
      }

      // Attach new files as documents
      const files = (req as any).files as Express.Multer.File[] | undefined;
      const category = (req.body?.category as string) || 'SUPPORTING';
      const docs: any[] = [];
      if (files && files.length > 0) {
        const actualRequestId = (workflow as any).requestId;
        for (const file of files) {
          const buffer = fs.readFileSync(file.path);
          const checksum = crypto.createHash('sha256').update(buffer).digest('hex');
          const extension = path.extname(file.originalname).replace('.', '').toLowerCase();
          const doc = await Document.create({
            requestId: actualRequestId,
            uploadedBy: userId,
            fileName: path.basename(file.filename || file.originalname),
            originalFileName: file.originalname,
            fileType: extension,
            fileExtension: extension,
            fileSize: file.size,
            filePath: file.path,
            storageUrl: `/uploads/${path.basename(file.path)}`,
            mimeType: file.mimetype,
            checksum,
            isGoogleDoc: false,
            googleDocUrl: null as any,
            category: category || 'OTHER',
            version: 1,
            parentDocumentId: null as any,
            isDeleted: false,
            downloadCount: 0,
          } as any);
          docs.push(doc);
        }
      }

      ResponseHandler.success(res, { workflow, newDocuments: docs }, 'Workflow updated with documents', 200);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to update workflow', 400, errorMessage);
    }
  }

  async submitWorkflow(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params;
      const workflow = await workflowService.submitWorkflow(id);
      
      if (!workflow) {
        ResponseHandler.notFound(res, 'Workflow not found');
        return;
      }

      ResponseHandler.success(res, workflow, 'Workflow submitted successfully');
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      ResponseHandler.error(res, 'Failed to submit workflow', 400, errorMessage);
    }
  }
}
