
import { Request, Response } from 'express';
import { PMSchedule } from '../models/PMSchedule';
import { WorkOrder } from '../models/WorkOrder';
import { Contract } from '../models/Contract';
import { Asset } from '../models/Asset';
import { findProjectByIdOrCode } from '../utils/projectHelper';
import { createPMScheduleAlert } from '../services/alertService';
import mongoose from 'mongoose';

// Helper to calculate next due date
const calculateNextDue = (current: Date, frequency: string): Date => {
    const date = new Date(current);
    switch (frequency) {
        case 'Daily': date.setDate(date.getDate() + 1); break;
        case 'Weekly': date.setDate(date.getDate() + 7); break;
        case 'Monthly': date.setMonth(date.getMonth() + 1); break;
        case 'Quarterly': date.setMonth(date.getMonth() + 3); break;
        case 'Semi-Annually': date.setMonth(date.getMonth() + 6); break;
        case 'Annually': date.setFullYear(date.getFullYear() + 1); break;
        default: date.setMonth(date.getMonth() + 1); // Default to monthly
    }
    return date;
};

// Helper to check if contract type is O&M, EPC, or PPA
const isValidContractType = (contractType: string): boolean => {
    if (!contractType) return false;
    const normalized = contractType.trim().toUpperCase().replace(/[&-\s]/g, '');
    return normalized === 'OM' || normalized === 'EPC' || normalized === 'PPA';
};

export const getPMSchedules = async (req: Request, res: Response) => {
    try {
        const { projectId } = req.query;

        if (!projectId) {
            return res.status(400).json({ success: false, message: 'projectId is required' });
        }

        // Handle both ObjectId and string ID
        let projectObjectId: any;

        // Check if projectId is a valid MongoDB ObjectId
        if (mongoose.Types.ObjectId.isValid(projectId as string)) {
            projectObjectId = new mongoose.Types.ObjectId(projectId as string);
        } else {
            // Use helper function to find project without Mongoose casting errors
            const project = await findProjectByIdOrCode(projectId as string);
            if (!project) {
                return res.status(404).json({ success: false, message: `Project with ID or code "${projectId}" not found` });
            }
            projectObjectId = project._id;
        }

        let pmSchedules;
        // Query with both string and ObjectId to handle different storage formats
        if (typeof projectObjectId === 'string' && !mongoose.Types.ObjectId.isValid(projectObjectId)) {
            // String projectId - use native MongoDB query
            if (!mongoose.connection.db) {
                throw new Error('Database connection not established');
            }
            const pmSchedulesCollection = mongoose.connection.db.collection('pmschedules');
            // Try both string and ObjectId format
            const objectIdProjectId = mongoose.Types.ObjectId.isValid(projectObjectId) 
                ? new mongoose.Types.ObjectId(projectObjectId) 
                : null;
            const query: any = { 
                $or: [
                    { projectId: projectObjectId },
                    ...(objectIdProjectId ? [{ projectId: objectIdProjectId }] : [])
                ]
            };
            pmSchedules = await pmSchedulesCollection.find(query).sort({ nextDue: 1 }).toArray();
        } else {
            // ObjectId projectId - use Mongoose
            // Also try string format in case projectId is stored as string
            const stringProjectId = projectObjectId.toString();
            pmSchedules = await PMSchedule.find({
                $or: [
                    { projectId: projectObjectId },
                    { projectId: stringProjectId }
                ]
            }).sort({ nextDue: 1 });
        }

        res.json({
            success: true,
            data: pmSchedules
        });
    } catch (error: any) {
        res.status(500).json({ success: false, message: error.message });
    }
};

export const createPMSchedule = async (req: Request, res: Response) => {
    try {
        const { contractId } = req.body;
        
        // Validate that contract exists and is O&M, EPC, or PPA contract
        if (contractId) {
            const contract = await Contract.findById(contractId);
            if (!contract) {
                return res.status(400).json({ success: false, message: "Hợp đồng không tồn tại" });
            }
            if (!isValidContractType(contract.contractType)) {
                return res.status(400).json({ success: false, message: "Chỉ có thể tạo lịch bảo trì cho hợp đồng O&M, EPC hoặc PPA" });
            }
        }
        
        const schedule = new PMSchedule(req.body);
        await schedule.save();

        // Create alert if schedule is due soon
        if (schedule.nextDue && schedule.status === 'Active') {
            await createPMScheduleAlert(
                schedule.title,
                schedule.nextDue,
                schedule._id.toString(),
                schedule.projectId?.toString()
            );
        }

        res.json({ success: true, data: schedule, message: "Tạo lịch bảo trì thành công" });
    } catch (error: any) {
        res.status(500).json({ success: false, message: error.message });
    }
};

export const updatePMSchedule = async (req: Request, res: Response) => {
    try {
        const { id } = req.params;
        const { contractId } = req.body;
        
        // Validate that contract exists and is O&M, EPC, or PPA contract if contractId is being updated
        if (contractId) {
            const contract = await Contract.findById(contractId);
            if (!contract) {
                return res.status(400).json({ success: false, message: "Hợp đồng không tồn tại" });
            }
            if (!isValidContractType(contract.contractType)) {
                return res.status(400).json({ success: false, message: "Chỉ có thể liên kết với hợp đồng O&M, EPC hoặc PPA" });
            }
        }
        
        const updatedSchedule = await PMSchedule.findByIdAndUpdate(id, req.body, { new: true });
        if (!updatedSchedule) {
            return res.status(404).json({ success: false, message: "PMSchedule not found" });
        }

        // Create alert if schedule is due soon
        if (updatedSchedule.nextDue && updatedSchedule.status === 'Active') {
            await createPMScheduleAlert(
                updatedSchedule.title,
                updatedSchedule.nextDue,
                updatedSchedule._id.toString(),
                updatedSchedule.projectId?.toString()
            );
        }

        res.json({ success: true, data: updatedSchedule, message: "Cập nhật lịch bảo trì thành công" });
    } catch (error: any) {
        res.status(500).json({ success: false, message: error.message });
    }
};

export const deletePMSchedule = async (req: Request, res: Response) => {
    try {
        const { id } = req.params;
        const deletedSchedule = await PMSchedule.findByIdAndDelete(id);
        if (!deletedSchedule) {
            return res.status(404).json({ success: false, message: "PMSchedule not found" });
        }
        res.json({ success: true, message: "Xóa lịch bảo trì thành công" });
    } catch (error: any) {
        res.status(500).json({ success: false, message: error.message });
    }
};

export const runScheduler = async (req: Request, res: Response) => {
    try {
        // 1. Find schedules due in the next 7 days
        const today = new Date();
        const nextWeek = new Date();
        nextWeek.setDate(today.getDate() + 7);

        const dueSchedules = await PMSchedule.find({
            status: 'Active',
            nextDue: { $lte: nextWeek }
        });

        let generatedCount = 0;

        // Create alerts for schedules due soon
        for (const schedule of dueSchedules) {
            try {
                await createPMScheduleAlert(
                    schedule.title,
                    schedule.nextDue,
                    schedule._id.toString(),
                    schedule.projectId?.toString()
                );
            } catch (alertError) {
                console.error(`Error creating alert for PM Schedule ${schedule._id}:`, alertError);
            }
        }

        for (const schedule of dueSchedules) {
            try {
                let assetId: any = null;
                
                // Step 1: Try to use schedule.assetId if it exists and is valid
                if (schedule.assetId) {
                    const existingAsset = await Asset.findById(schedule.assetId);
                    if (existingAsset) {
                        assetId = existingAsset._id;
                    }
                }
                
                // Step 2: If no valid assetId, find Plant asset from project
                if (!assetId) {
                    const plantAsset = await Asset.findOne({
                        projectId: schedule.projectId,
                        assetType: 'Plant'
                    });
                    
                    if (plantAsset && plantAsset._id) {
                        assetId = plantAsset._id;
                    }
                }
                
                // Step 3: If still no assetId, get first asset of the project
                if (!assetId) {
                    const firstAsset = await Asset.findOne({
                        projectId: schedule.projectId
                    });
                    
                    if (firstAsset && firstAsset._id) {
                        assetId = firstAsset._id;
                    }
                }
                
                // Step 4: assetId is optional - Work Order can be created without assetId
                // Work Order will be linked to contractId instead

                // Extract assignedTo from schedule - copy ALL user IDs (not just first one)
                // assignedTo will contain comma-separated user IDs from schedule
                // assignedTechnicianId is set to first user ID for backward compatibility
                let assignedTo: string | undefined = undefined;
                let assignedTechnicianId: string | undefined = undefined;
                
                if (schedule.assignedTo) {
                    // Convert to string if not already a string
                    const assignedToStr = typeof schedule.assignedTo === 'string' 
                        ? schedule.assignedTo 
                        : String(schedule.assignedTo);
                    
                    const trimmed = assignedToStr.trim();
                    if (trimmed) {
                        // Copy ALL user IDs to assignedTo (comma-separated format)
                        assignedTo = trimmed;
                        
                        // Parse all user IDs from comma-separated string
                        const allUserIds = trimmed.includes(',') 
                            ? trimmed.split(',').map(id => id.trim()).filter(id => id)
                            : [trimmed];
                        
                        // Set first user ID for backward compatibility (assignedTechnicianId field)
                        assignedTechnicianId = allUserIds.length > 0 ? allUserIds[0] : undefined;
                        
                        // Debug log to verify all users are being copied
                        console.log(`[PM Scheduler] Schedule ${schedule._id}: Copying ${allUserIds.length} users to Work Order:`, allUserIds);
                        console.log(`[PM Scheduler] assignedTo value:`, assignedTo);
                    }
                }

                const wo = new WorkOrder({
                    woCode: `WO-${Date.now()}-${Math.floor(Math.random() * 1000)}`,
                    projectId: schedule.projectId,
                    contractId: schedule.contractId,
                    assetId: assetId || undefined, // Optional: assetId can be undefined
                    workOrderType: 'PM',
                    title: `Bảo trì định kỳ: ${schedule.title}`,
                    description: schedule.description || `Được tạo tự động từ lịch bảo trì KHĐK.`,
                    priority: 'Medium',
                    status: 'Draft',
                    scheduledStart: schedule.nextDue,
                    scheduledEnd: new Date(new Date(schedule.nextDue).setHours(schedule.nextDue.getHours() + 2)),
                    assignedTechnicianId: assignedTechnicianId,
                    assignedTo: assignedTo,
                    costLabor: 0,
                    costMaterial: 0,
                    costExternal: 0
                });
                
                await wo.save();

                // Update Schedule
                schedule.lastPerformed = new Date();
                schedule.nextDue = calculateNextDue(schedule.nextDue, schedule.frequency);
                await schedule.save();

                generatedCount++;
            } catch (scheduleError: any) {
                // Log error for this specific schedule but continue with others
                console.error(`Error processing PM Schedule ${schedule._id}:`, scheduleError.message, scheduleError.stack);
                continue;
            }
        }

        res.json({
            success: true,
            message: 'Scheduler executed successfully',
            data: {
                createdCount: generatedCount
            }
        });
    } catch (error: any) {
        res.status(500).json({ success: false, message: error.message });
    }
};
