const multer = require('multer');
const path = require('path');
const fs = require('fs');
const Prescription = require('../models/Prescription');
const { validationResult } = require('express-validator');
const asyncHandler = require('../middleware/async');

// Ensure uploads directory exists
const uploadDir = 'uploads/prescriptions';
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir, { recursive: true });
}

// Configure multer for file uploads
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, uploadDir);
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    const ext = path.extname(file.originalname).toLowerCase();
    cb(null, 'rx-' + uniqueSuffix + ext);
  }
});

const fileFilter = (req, file, cb) => {
  // Accept images and PDFs
  if (file.mimetype.startsWith('image/') || file.mimetype === 'application/pdf') {
    cb(null, true);
  } else {
    cb(new Error('Only image files (jpg, jpeg, png, gif) and PDFs are allowed!'), false);
  }
};

// Configure multer for file uploads
const upload = multer({ 
  storage: storage,
  limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit
  fileFilter: fileFilter
});

// Handle file upload middleware
const handleUpload = (req, res, next) => {
  const uploadSingle = upload.single('prescriptionImage');
  
  uploadSingle(req, res, (err) => {
    if (err instanceof multer.MulterError) {
      // A Multer error occurred when uploading
      return res.status(400).json({
        success: false,
        message: 'File upload error',
        error: err.message
      });
    } else if (err) {
      // An unknown error occurred
      return res.status(500).json({
        success: false,
        message: 'Server error during file upload',
        error: err.message
      });
    }
    
    // No errors, proceed to the next middleware
    next();
  });
};

// @desc    Upload a new prescription
// @route   POST /api/v1/prescriptions/upload
// @access  Private
const uploadPrescription = asyncHandler(async (req, res) => {
  if (!req.files || req.files.length === 0) {
    return res.status(400).json({ 
      success: false, 
      msg: 'Please upload at least one image or PDF' 
    });
  }

  // Map uploaded files to the response
  const uploadedFiles = req.files.map(file => ({
    filename: file.filename,
    path: file.path.replace(/\\/g, '/'), // Convert backslashes to forward slashes for URLs
    mimetype: file.mimetype,
    size: file.size
  }));

  res.status(200).json({
    success: true,
    data: uploadedFiles
  });
});

// @desc    Create a new prescription
// @route   POST /api/prescriptions
// @access  Private
const createPrescription = asyncHandler(async (req, res) => {
  const errors = validationResult(req);
  
  try {
    // First check for validation errors
    if (!errors.isEmpty()) {
      // Clean up any uploaded files if validation fails
      if (req.files && req.files.length > 0) {
        req.files.forEach(file => {
          if (fs.existsSync(file.path)) {
            fs.unlinkSync(file.path);
          }
        });
      }
      return res.status(400).json({ 
        success: false, 
        errors: errors.array() 
      });
    }

    // Ensure we have files uploaded
    if (!req.files || req.files.length === 0) {
      return res.status(400).json({
        success: false,
        error: 'Please upload at least one prescription image or PDF'
      });
    }

    const { 
      doctor_name,
      patient_name,
      patient_age,
      patient_gender,
      diagnosis,
      notes,
      prescription_date,
      next_visit_date
    } = req.body;

    // Process uploaded files
    const imageData = [];
    if (req.files && req.files.length > 0) {
      // Create the uploads/prescriptions directory if it doesn't exist
      const uploadDir = 'uploads/prescriptions';
      if (!fs.existsSync(uploadDir)) {
        fs.mkdirSync(uploadDir, { recursive: true });
      }

      // Process each file
      req.files.forEach((file, index) => {
        // Create a relative path for the database (relative to the 'uploads' directory)
        const relativePath = path.relative('uploads', file.path).replace(/\\/g, '/');
        
        imageData.push({
          path: relativePath,
          is_primary: index === 0, // First image is primary
          uploaded_at: new Date(),
          mimetype: file.mimetype,
          size: file.size
        });
      });
    }

    // Create prescription data
    const prescriptionData = {
      user_id: req.user.id,
      doctor_name: doctor_name || 'Self',
      patient_name,
      patient_age: parseInt(patient_age, 10),
      patient_gender,
      diagnosis: diagnosis || '',
      notes: notes || '',
      images: imageData,
      prescription_date: prescription_date || new Date(),
      next_visit_date: next_visit_date || null,
      status: 'pending'
    };

    // Save to database
    const prescription = await Prescription.create(prescriptionData);
    
    res.status(201).json({
      success: true,
      data: prescription,
      msg: 'Prescription uploaded successfully. It will be reviewed by our pharmacist.'
    });
    
  } catch (error) {
    console.error('Error creating prescription:', error);
    
    // Clean up uploaded files if there was an error
    if (req.files && req.files.length > 0) {
      req.files.forEach(file => {
        if (fs.existsSync(file.path)) {
          fs.unlinkSync(file.path);
        }
      });
    }
    
    res.status(500).json({
      success: false,
      error: 'Failed to create prescription',
      message: error.message
    });
    
    res.status(500).json({
      success: false,
      error: 'Failed to create prescription',
      message: error.message
    });
  }
});

// @desc    Get user's prescriptions
// @route   GET /api/prescriptions/my-prescriptions
// @access  Private
const getMyPrescriptions = asyncHandler(async (req, res) => {
  const { status } = req.query;
  const prescriptions = await Prescription.findByUser(req.user.id, status);
  
  res.status(200).json({
    success: true,
    count: prescriptions.length,
    data: prescriptions
  });
});

// @desc    Get prescription by ID
// @route   GET /api/prescriptions/:id
// @access  Private
// @desc    Get prescription image
// @route   GET /api/v1/prescriptions/image/:filename
// @access  Private
const getPrescriptionImage = asyncHandler(async (req, res) => {
  const { filename } = req.params;
  const filePath = path.join(__dirname, '..', 'uploads', 'prescriptions', filename);
  
  // Check if file exists
  if (!fs.existsSync(filePath)) {
    return res.status(404).json({
      success: false,
      msg: 'Image not found'
    });
  }

  // Check if user is authorized to access this image
  const prescription = await Prescription.findOne({
    'images.path': { $regex: filename }
  });

  if (!prescription) {
    return res.status(404).json({
      success: false,
      msg: 'Prescription not found'
    });
  }

  // If the user is not an admin and doesn't own the prescription
  if (req.user.role !== 'admin' && prescription.user_id.toString() !== req.user.id) {
    return res.status(403).json({
      success: false,
      msg: 'Not authorized to access this image'
    });
  }

  // Send the file
  return res.sendFile(filePath);
});

const getPrescription = asyncHandler(async (req, res) => {
  const prescription = await Prescription.findByIdWithDetails(req.params.id);
  
  if (!prescription) {
    return res.status(404).json({
      success: false,
      msg: 'Prescription not found'
    });
  }

  // Check if user has permission to view this prescription
  if (req.user.role === 'user' && prescription.user_id._id.toString() !== req.user.id) {
    return res.status(403).json({
      success: false,
      msg: 'Not authorized to view this prescription'
    });
  }

  res.status(200).json({
    success: true,
    data: prescription
  });
});

// @desc    Update prescription status (for pharmacist)
// @route   PUT /api/prescriptions/:id/status
// @access  Private/Pharmacist
const updatePrescriptionStatus = asyncHandler(async (req, res) => {
  const { status, rejection_reason, pharmacist_notes } = req.body;
  
  // Validate status
  if (!['approved', 'rejected'].includes(status)) {
    return res.status(400).json({
      success: false,
      msg: 'Invalid status. Must be either "approved" or "rejected"'
    });
  }

  // If rejecting, require a reason
  if (status === 'rejected' && !rejection_reason) {
    return res.status(400).json({
      success: false,
      msg: 'Rejection reason is required when rejecting a prescription'
    });
  }

  const prescription = await Prescription.updateStatus(req.params.id, {
    status,
    rejection_reason,
    pharmacist_notes,
    reviewed_by: req.user.id
  });

  if (!prescription) {
    return res.status(404).json({
      success: false,
      msg: 'Prescription not found'
    });
  }

  // Here you would typically send an email notification to the user
  // about their prescription status update

  res.status(200).json({
    success: true,
    data: prescription,
    msg: `Prescription ${status} successfully`
  });
});

// @desc    Get all prescriptions with optional filtering
// @route   GET /api/prescriptions
// @access  Private/Admin/Pharmacist
const getAllPrescriptions = asyncHandler(async (req, res) => {
  const { status, search } = req.query;
  
  const prescriptions = await Prescription.getAllPrescriptions({
    status,
    search: search || ''
  });
  
  res.status(200).json({
    success: true,
    count: prescriptions.length,
    data: prescriptions
  });
});

// @desc    Get pending prescriptions (for pharmacist)
// @route   GET /api/prescriptions/pending
// @access  Private/Pharmacist
const getPendingPrescriptions = asyncHandler(async (req, res) => {
  const prescriptions = await Prescription.findPending();
  
  res.status(200).json({
    success: true,
    count: prescriptions.length,
    data: prescriptions
  });
});

// Configure upload for multiple files (up to 5)
const uploadMultiple = upload.array('files', 5);

// @desc    Get prescriptions for a specific user
// @route   GET /api/v1/prescriptions/user/:userId
// @access  Private
const getUserPrescriptions = asyncHandler(async (req, res) => {
  const { userId } = req.params;
  
  // Check if the user is authorized to view these prescriptions
  if (req.user.role !== 'admin' && req.user.id !== userId) {
    return res.status(403).json({
      success: false,
      msg: 'Not authorized to view these prescriptions'
    });
  }

  // Use the Mongoose model directly to ensure we get a proper query object
  const mongoose = require('mongoose');
  const PrescriptionModel = mongoose.model('Prescription');
  
  const prescriptions = await PrescriptionModel
    .find({ user_id: userId })
    .sort({ createdAt: -1 })
    .populate('user_id', 'name email')
    .populate('reviewed_by', 'name email')
    .exec();

  res.status(200).json({
    success: true,
    count: prescriptions.length,
    data: prescriptions
  });
});

// @desc    Delete a prescription
// @route   DELETE /api/v1/prescriptions/:id
// @access  Private/Admin
// @desc    Get prescriptions by status
// @route   GET /api/v1/prescriptions/status/:status
// @access  Private/Admin
const getPrescriptionsByStatus = asyncHandler(async (req, res) => {
  const { status } = req.params;
  const { page = 1, limit = 10 } = req.query;

  // Validate status
  const validStatuses = ['pending', 'verified', 'rejected', 'fulfilled'];
  if (!validStatuses.includes(status)) {
    return res.status(400).json({
      success: false,
      message: 'Invalid status. Must be one of: ' + validStatuses.join(', ')
    });
  }

  // Set up pagination
  const pageNumber = parseInt(page, 10);
  const pageSize = parseInt(limit, 10);
  const skip = (pageNumber - 1) * pageSize;

  // Build query
  const query = { status };
  
  // Check if user is a pharmacist, only show their verified prescriptions
  if (req.user.role === 'pharmacist' && status === 'verified') {
    query.verifiedBy = req.user.id;
  }

  // Get prescriptions with pagination
  const [prescriptions, total] = await Promise.all([
    Prescription.find(query)
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(pageSize)
      .populate('user', 'name email phone')
      .populate('verifiedBy', 'name')
      .lean(),
    Prescription.countDocuments(query)
  ]);

  const totalPages = Math.ceil(total / pageSize);

  res.status(200).json({
    success: true,
    count: prescriptions.length,
    total,
    page: pageNumber,
    totalPages,
    data: prescriptions
  });
});

const deletePrescription = asyncHandler(async (req, res) => {
  const prescription = await Prescription.findById(req.params.id);

  if (!prescription) {
    return res.status(404).json({
      success: false,
      message: 'Prescription not found'
    });
  }

  // Delete associated files
  if (prescription.images && prescription.images.length > 0) {
    for (const image of prescription.images) {
      const filePath = path.join(__dirname, '..', 'uploads', image.path);
      if (fs.existsSync(filePath)) {
        fs.unlinkSync(filePath);
      }
    }
  }

  await prescription.remove();

  res.status(200).json({
    success: true,
    data: {},
    message: 'Prescription deleted successfully'
  });
});

module.exports = {
  handleUpload,
  uploadPrescription,
  uploadPrescriptionImages: uploadPrescription, // Alias for backward compatibility
  getMyPrescriptions,
  getPrescriptionById: getPrescription, // Alias for backward compatibility
  getPrescription, // The actual function
  getPrescriptionsByStatus,
  updatePrescriptionStatus,
  deletePrescription,
  getPrescriptionImage,
  upload, // For backward compatibility
  getAllPrescriptions
};