const mongoose = require('mongoose');
const BaseModel = require('./BaseModel');
const Prescription = require('./Prescription');

// Prescription details schema (moved from Prescription model)
const prescriptionDetailsSchema = new mongoose.Schema({
  // Basic prescription info
  doctor_name: { 
    type: String,
    required: false 
  },
  patient_name: { 
    type: String,
    required: false 
  },
  patient_age: { 
    type: Number,
    required: false,
    min: 0,
    max: 120
  },
  patient_gender: { 
    type: String,
    enum: ['male', 'female', 'other', null],
    required: false 
  },
  diagnosis: String,
  notes: String,
  
  // Prescription images
  images: [{
    path: { 
      type: String, 
      required: true 
    },
    is_primary: { 
      type: Boolean, 
      default: false 
    },
    uploaded_at: { 
      type: Date, 
      default: Date.now 
    },
    mimetype: { 
      type: String, 
      required: true 
    },
    size: { 
      type: Number, 
      required: true 
    },
    original_name: String
  }],
  
  // Prescription dates
  prescription_date: { 
    type: Date, 
    default: Date.now,
    required: false
  },
  next_visit_date: {
    type: Date,
    required: false
  },
  
  // Verification details
  status: {
    type: String,
    enum: ['pending', 'in_review', 'approved', 'rejected', 'expired'],
    default: 'pending',
    required: false
  },
  is_verified: {
    type: Boolean,
    default: false,
    required: false
  },
  verification_method: {
    type: String,
    enum: ['manual', 'auto', 'third_party'],
    default: 'manual',
    required: false
  },
  reviewed_by: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: false
  },
  reviewed_at: {
    type: Date,
    required: false
  },
  rejection_reason: {
    type: String,
    required: false
  },
  
  // Verification history
  verification_history: [{
    status: { 
      type: String, 
      required: false 
    },
    changed_by: { 
      type: mongoose.Schema.Types.ObjectId, 
      ref: 'User',
      required: false
    },
    notes: {
      type: String,
      required: false
    },
    changed_at: { 
      type: Date, 
      default: Date.now 
    }
  }],
  
  // Prescription items
  items: [{
    item_id: { 
      type: mongoose.Schema.Types.ObjectId, 
      ref: 'Item' 
    },
    name: String,
    dosage: String,
    frequency: String,
    duration: String,
    instructions: String,
    quantity: Number
  }]
}, { _id: false, timestamps: true });

// Order item schema
const orderItemSchema = new mongoose.Schema({
  item: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Item',
    required: [true, 'Item ID is required'],
    alias: 'id'
  },
  productId: { 
    type: mongoose.Schema.Types.ObjectId, 
    ref: 'Item',
    required: false,
    default: function() {
      return this.item; // Default to the same value as the item field
    }
  },
  name: {
    type: String,
    required: [true, 'Item name is required']
  },
  price: {
    type: Number,
    required: [true, 'Price is required'],
    min: [0, 'Price cannot be negative']
  },
  quantity: {
    type: Number,
    required: [true, 'Quantity is required'],
    min: [1, 'Quantity must be at least 1']
  },
  requiresPrescription: {
    type: Boolean,
    default: false
  },
  image: {
    type: String,
    default: ''
  },
  brand: {
    type: String,
    default: 'Generic'
  },
  category: { 
    type: String,
    default: 'General'
  },
  unit: { 
    type: String,
    default: 'pcs'
  },
  taxAmount: {
    type: Number,
    default: 0,
    min: [0, 'Tax amount cannot be negative']
  },
  discountAmount: {
    type: Number,
    default: 0,
    min: [0, 'Discount amount cannot be negative']
  },
  totalAmount: {
    type: Number,
    required: false,
    min: [0, 'Total amount cannot be negative'],
    default: function() {
      const price = this.price || 0;
      const quantity = this.quantity || 1;
      const tax = this.taxAmount || 0;
      const discount = this.discountAmount || 0;
      return (price * quantity) + tax - discount;
    }
  },
  weight: {
    type: Number,
    default: 0,
    min: [0, 'Weight cannot be negative']
  },
  sku: {
    type: String,
    default: ''
  },
  barcode: {
    type: String,
    default: ''
  }
}, { _id: false, timestamps: true });

// Order address schema
const orderAddressSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'Recipient name is required'],
    trim: true
  },
  contactNumber: {
    type: String,
    required: false,
    trim: true,
    default: ''
  },
  address: {
    type: String,
    required: [true, 'Address is required'],
    trim: true
  },
  landmark: {
    type: String,
    default: '',
    trim: true
  },
  city: {
    type: String,
    required: [true, 'City is required'],
    trim: true
  },
  state: {
    type: String,
    required: [true, 'State is required'],
    trim: true
  },
  postalCode: {
    type: String,
    required: [true, 'Postal code is required'],
    trim: true,
    validate: {
      validator: function(v) {
        return /^\d{6}$/.test(v) || v === '';
      },
      message: props => `${props.value} is not a valid postal code! Please provide a 6-digit code or leave it empty.`
    }
  },
  country: {
    type: String,
    default: 'India',
    trim: true
  },
  type: {
    type: String,
    enum: ['home', 'work', 'other'],
    default: 'home'
  },
  isDefault: {
    type: Boolean,
    default: false
  }
}, { _id: false });

// Order schema
const statusHistorySchema = new mongoose.Schema({
  status: {
    type: String,
    required: true
  },
  changedAt: {
    type: Date,
    default: Date.now
  },
  comment: {
    type: String,
    default: ''
  }
}, { _id: false });

const orderSchema = new mongoose.Schema({
  userId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: [true, 'User ID is required']
  },
  orderNumber: {
    type: String,
    unique: true,
    required: [true, 'Order number is required']
  },
  store: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Store',
    required: [true, 'Store ID is required']
  },
  items: [orderItemSchema],
  totalAmount: {
    type: Number,
    required: false,
    min: [0, 'Total amount cannot be negative'],
    default: function() {
      const price = this.price || 0;
      const quantity = this.quantity || 1;
      const tax = this.taxAmount || 0;
      const discount = this.discountAmount || 0;
      return (price * quantity) + tax - discount;
    }
  },
  discountAmount: {
    type: Number,
    default: 0,
    min: [0, 'Discount amount cannot be negative']
  },
  taxAmount: {
    type: Number,
    default: 0,
    min: [0, 'Tax amount cannot be negative']
  },
  finalAmount: {
    type: Number,
    required: [true, 'Final amount is required'],
    min: [0, 'Final amount cannot be negative']
  },
  address: orderAddressSchema,
  paymentId: {
    type: String,
    required: [true, 'Payment ID is required']
  },
  status: {
    type: String,
    enum: ['PENDING', 'PENDING_VERIFICATION', 'CONFIRMED', 'PROCESSING', 'SHIPPED', 'DELIVERED', 'CANCELLED', 'REFUNDED', 'REJECTED'],
    default: 'PENDING'
  },
  paymentStatus: {
    type: String,
    enum: ['PENDING', 'PAID', 'FAILED', 'REFUNDED', 'PARTIALLY_REFUNDED'],
    default: 'PENDING'
  },
  paymentMethod: {
    type: String,
    enum: ['cod', 'online', 'wallet'],
    required: [true, 'Payment method is required']
  },
  deliveryBoy: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  },
  storeManager: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User'
  },
  deliveryInfo: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'DeliveryInfo'
  },
  statusHistory: [statusHistorySchema],
  notes: String,
  cancellationReason: String,
  cancellationDetails: {
    reason: String,
    notes: String,
    cancelledBy: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'User'
    },
    cancelledAt: Date
  },
  
  // Combined prescription data
  prescription: {
    type: prescriptionDetailsSchema,
    required: [
      function() {
        return this.items.some(item => item.requiresPrescription);
      },
      'Prescription is required for orders with prescription items'
    ]
  },
  prescriptionId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Prescription'
  },
  prescriptionStatus: {
    type: String,
    enum: ['not_required', 'pending', 'approved', 'rejected'],
    default: 'not_required'
  },
  is_verified: {
    type: Boolean,
    default: false
  },
  verification_method: {
    type: String,
    enum: ['manual', 'auto', 'third_party'],
    default: 'manual'
  }
}, { 
  timestamps: true,
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
});

// Pre-save hook to sync order status with prescription status
orderSchema.pre('save', function(next) {
  // Only run this if prescription is being modified
  if (this.isModified('prescription') && this.prescription) {
    // If prescription is approved, update order status
    if (this.prescription.status === 'approved' && this.status !== 'CONFIRMED') {
      this.status = 'CONFIRMED';
      this.statusHistory.push({
        status: 'CONFIRMED',
        changedAt: new Date(),
        comment: 'Order confirmed after prescription approval'
      });
    }
    // If prescription is rejected, update order status
    else if (this.prescription.status === 'rejected' && this.status !== 'REJECTED') {
      this.status = 'REJECTED';
      this.statusHistory.push({
        status: 'REJECTED',
        changedAt: new Date(),
        comment: `Order rejected - ${this.prescription.rejection_reason || 'Prescription verification failed'}`
      });
      
      // If payment was made, update payment status to REFUNDED
      if (this.paymentStatus === 'PAID') {
        this.paymentStatus = 'REFUNDED';
      }
    }
  }
  next();
});

/**
 * Verify prescription for the order
 * @param {Object} options - Verification options
 * @param {String} options.status - Verification status ('approved' or 'rejected')
 * @param {String} options.reviewedBy - User ID who is verifying the prescription
 * @param {String} options.notes - Additional notes for the verification
 * @param {String} [options.rejectionReason] - Required if status is 'rejected'
 * @returns {Promise<Order>} - The updated order
 */
orderSchema.methods.verifyPrescription = async function({ status, reviewedBy, notes, rejectionReason }) {
  if (!['approved', 'rejected'].includes(status)) {
    throw new Error('Invalid status. Must be "approved" or "rejected"');
  }
  
  if (status === 'rejected' && !rejectionReason) {
    throw new Error('Rejection reason is required when rejecting a prescription');
  }
  
  // Update prescription status
  this.prescription.status = status;
  this.prescription.reviewed_by = reviewedBy;
  this.prescription.reviewed_at = new Date();
  this.prescription.is_verified = status === 'approved';
  
  if (status === 'rejected') {
    this.prescription.rejection_reason = rejectionReason;
  }
  
  // Add to verification history
  this.prescription.verification_history.push({
    status,
    changed_by: reviewedBy,
    notes: status === 'rejected' ? `Rejection reason: ${rejectionReason}` : (notes || 'Prescription verified'),
    changed_at: new Date()
  });
  
  // The pre-save hook will handle the order status update
  return this.save();
};

/**
 * Check if order requires prescription verification
 * @returns {Boolean} - True if order requires prescription verification
 */
orderSchema.methods.requiresPrescriptionVerification = function() {
  return this.items.some(item => item.requiresPrescription) && this.prescription?.status === 'pending';
};

/**
 * Get orders that require prescription verification
 * @param {Object} [filter] - Additional filter criteria
 * @returns {Promise<Array>} - Array of orders requiring verification
 */
orderSchema.statics.findOrdersRequiringVerification = async function(filter = {}) {
  return this.find({
    'items.requiresPrescription': true,
    'prescription.status': 'pending',
    ...filter
  })
  .populate('userId', 'name email phone')
  .populate('items.item', 'name')
  .sort({ createdAt: -1 });
};

orderSchema.statics.findOrdersRequiringPrescriptionVerification = async function(filter = {}) {
  return this.find({
    ...filter,
    'items.requiresPrescription': true,
    $or: [
      { prescriptionStatus: 'pending' },
      { prescriptionStatus: { $exists: false } }
    ]
  })
  .populate('userId', 'name email phone')
  .populate('prescriptionId')
  .sort({ createdAt: -1 });
};

// Check if any items in the order require a prescription
orderSchema.methods.checkPrescriptionRequirements = function() {
  return this.items.some(item => item.requiresPrescription);
};

orderSchema.methods.updatePrescriptionStatus = async function(status, { reviewedBy, rejectionReason } = {}) {
  // Update the order's prescription status
  this.prescriptionStatus = status;
  
  // If we have a prescription ID, update the prescription as well
  if (this.prescriptionId) {
    const Prescription = require('./Prescription');
    await Prescription.updateStatus(this.prescriptionId, { 
      status, 
      reviewedBy, 
      rejectionReason 
    });
  }
  
  // Update order status based on prescription status
  if (status === 'approved') {
    this.status = 'CONFIRMED';
  } else if (status === 'rejected') {
    this.status = 'REJECTED';
    if (rejectionReason) {
      this.rejectionReason = rejectionReason;
    }
  } else if (status === 'pending') {
    this.status = 'PENDING_VERIFICATION';
  }
  
  // Add to status history
  this.statusHistory = this.statusHistory || [];
  this.statusHistory.push({
    status: this.status,
    changedAt: new Date(),
    comment: `Prescription ${status}` + (rejectionReason ? ` - ${rejectionReason}` : '')
  });
  
  return this.save();
};

// Add text index for search
orderSchema.index({
  'orderNumber': 'text',
  'items.name': 'text',
  'user.name': 'text',
  'user.email': 'text',
  'deliveryAddress.address': 'text',
  'deliveryAddress.city': 'text',
  'deliveryAddress.state': 'text',
  'deliveryAddress.country': 'text',
  'payment.transactionId': 'text',
  'prescription.doctor_name': 'text',
  'prescription.diagnosis': 'text'
});

// Add methods and statics to the schema
class Order extends BaseModel {
  static get schema() {
    return orderSchema;
  }

  static init() {
    if (mongoose.models.Order) {
      return mongoose.models.Order;
    }
    return mongoose.model('Order', orderSchema);
  }

  /**
   * Create a new order with transaction support
   * @param {Object} orderData - Order data
   * @returns {Promise<Order>} Created order
   */
  static async createOrder(orderData) {
    const session = await mongoose.startSession();
    session.startTransaction();
    
    try {
      const order = await this.create([orderData], { session });
      await session.commitTransaction();
      return order[0];
    } catch (error) {
      await session.abortTransaction();
      throw error;
    } finally {
      session.endSession();
    }
  }

  /**
   * Get order details with populated data
   * @param {String} orderId - Order ID
   * @returns {Promise<Object>} Order details
   */
  static async getOrderDetails(orderId) {
    return this.findById(orderId)
      .populate('userId', 'name email phone')
      .populate('items.item', 'name price image')
      .populate('deliveryInfo.deliveryBoy', 'name phone')
      .lean();
  }

  /**
   * Get user orders with pagination and filtering
   * @param {String} userId - User ID
   * @param {Object} options - Query options
   * @param {String} options.status - Filter by status
   * @param {Number} options.limit - Number of results per page
   * @param {Number} options.offset - Number of documents to skip
   * @returns {Promise<Object>} Paginated orders
   */
  static async getUserOrders(userId, { status, limit = 10, offset = 0 } = {}) {
    const query = { userId };
    if (status) query.status = status;

    const [orders, total] = await Promise.all([
      this.find(query)
        .sort({ createdAt: -1 })
        .skip(offset)
        .limit(limit)
        .populate('items.item', 'name price image')
        .lean(),
      this.countDocuments(query)
    ]);

    return {
      data: orders,
      pagination: {
        total,
        limit,
        offset,
        hasMore: offset + orders.length < total
      }
    };
  }

  /**
   * Update order status
   * @param {String} orderId - Order ID
   * @param {String} status - New status
   * @param {Object} options - Additional options
   * @param {String} options.comment - Status change comment
   * @param {String} options.updatedBy - User ID who updated the status
   * @returns {Promise<Order>} Updated order
   */
  static async updateStatus(orderId, status, { comment, updatedBy } = {}) {
    const update = { status };
    
    if (comment || updatedBy) {
      update.$push = {
        statusHistory: {
          status,
          comment,
          changedBy: updatedBy,
          changedAt: new Date()
        }
      };
    }

    return this.findByIdAndUpdate(
      orderId,
      update,
      { new: true, runValidators: true }
    );
  }

  /**
   * Update payment status
   * @param {String} orderId - Order ID
   * @param {String} status - New payment status
   * @param {Object} paymentDetails - Payment details
   * @returns {Promise<Order>} Updated order
   */
  static async updatePaymentStatus(orderId, status, paymentDetails = {}) {
    const update = { 
      paymentStatus: status,
      $push: {
        paymentHistory: {
          status,
          ...paymentDetails,
          updatedAt: new Date()
        }
      }
    };

    return this.findByIdAndUpdate(
      orderId,
      update,
      { new: true, runValidators: true }
    );
  }

  /**
   * Assign delivery boy to order
   * @param {String} orderId - Order ID
   * @param {String} deliveryBoyId - Delivery boy ID
   * @returns {Promise<Order>} Updated order
   */
  static async assignDeliveryBoy(orderId, deliveryBoyId) {
    return this.findByIdAndUpdate(
      orderId,
      { 
        'deliveryInfo.deliveryBoy': deliveryBoyId,
        'deliveryInfo.assignedAt': new Date()
      },
      { new: true, runValidators: true }
    );
  }
}

// Create and export the model
module.exports = Order.init();
