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

const categorySchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'Category name is required'],
    trim: true,
    maxlength: [100, 'Category name cannot exceed 100 characters']
  },
  description: {
    type: String,
    trim: true,
    default: null
  },
  code: {
    type: String,
    trim: true,
    maxlength: [50, 'Code cannot exceed 50 characters'],
    default: null
  },
  image: {
    type: String,
    trim: true,
    maxlength: [255, 'Image path cannot exceed 255 characters'],
    default: null
  },
  subtitle: {
    type: String,
    trim: true,
    default: null
  },
  status: {
    type: Boolean,
    default: true
  },
  position: {
    type: Number,
    default: 0
  }
}, {
  timestamps: true,
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
});

// Indexes for better performance
categorySchema.index({ name: 1 });
categorySchema.index({ code: 1 });
categorySchema.index({ status: 1 });
categorySchema.index({ position: 1 });
categorySchema.index({ name: 'text', description: 'text', subtitle: 'text' }); // Text search

// Virtual for getting items in this category
categorySchema.virtual('items', {
  ref: 'Item',
  localField: '_id',
  foreignField: 'categoryId'
});

class Category extends BaseModel {
  static get schema() {
    return categorySchema;
  }

  /**
   * Initialize the Category model
   */
  static init() {
    const model = super.init();
    
    // Drop old conflicting indexes
    if (model.collection) {
      // Drop old text index
      model.collection.dropIndex('name_text_description_text').catch(() => {
        // Ignore error if index doesn't exist
      });
      
      // Drop old slug index that's causing duplicate key errors
      model.collection.dropIndex('slug_1').catch(() => {
        // Ignore error if index doesn't exist
      });
      
      // Create new text index
      setTimeout(() => {
        model.collection.createIndex({
          name: 'text',
          description: 'text',
          subtitle: 'text'
        }).catch(err => {
          console.warn('Failed to create text index for Category:', err.message);
        });
      }, 100); // Small delay to ensure drops complete
    }
    
    return model;
  }

  /**
   * Get all categories with pagination and search
   */
  /**
   * Update a category by ID with proper validation
   */
  static async updateCategory(id, data) {
    // Ensure position is a number
    if (data.position !== undefined) {
      data.position = parseInt(data.position) || 0;
    }
    
    // Use findOneAndUpdate with runValidators to ensure validation runs
    const updated = await this.model.findOneAndUpdate(
      { _id: id },
      { $set: data },
      { new: true, runValidators: true }
    );
    
    if (!updated) {
      throw new Error('Category not found');
    }
    
    return updated;
  }

  static async findAllActive(options = {}) {
    const { page = 1, limit = 10, search } = options;

    let query = { status: true };

    // Add search filter
    if (search) {
      query.$text = { $search: search };
    }

    return this.paginate(query, {
      page,
      limit,
      sort: search ? { score: { $meta: 'textScore' } } : { position: 1, createdAt: -1 },
      select: 'name description code image subtitle status position createdAt updatedAt',
      lean: true
    });
  }

  /**
   * Get category by ID with associated items count
   */
  static async findByIdWithDetails(id) {
    return this.aggregate([
      {
        $match: {
          _id: new mongoose.Types.ObjectId(id)
        }
      },
      {
        $lookup: {
          from: 'items',
          let: { categoryId: '$_id' },
          pipeline: [
            {
              $match: {
                $expr: {
                  $and: [
                    { $eq: ['$categoryId', '$$categoryId'] },
                    { $eq: ['$status', true] }
                  ]
                }
              }
            },
            {
              $count: 'count'
            }
          ],
          as: 'itemStats'
        }
      },
      {
        $addFields: {
          itemCount: { $arrayElemAt: ['$itemStats.count', 0] },
          id: '$_id'
        }
      },
      {
        $project: {
          itemStats: 0,
          __v: 0
        }
      }
    ]).then(results => results[0] || null);
  }

  /**
   * Create new category with validation
   */
  static async createCategory(data) {
    const { name, description, code, image, subtitle } = data;

    // Check if category name already exists
    const existingCategory = await this.findOne({
      name: new RegExp(`^${name.trim()}$`, 'i')
    });

    if (existingCategory) {
      throw new Error('Category name already exists');
    }

    // Check if code already exists (if provided)
    if (code) {
      const existingCode = await this.findOne({
        code: new RegExp(`^${code.trim()}$`, 'i')
      });

      if (existingCode) {
        throw new Error('Category code already exists');
      }
    }

    return this.create({
      name: name.trim(),
      description: description ? description.trim() : null,
      code: code ? code.trim() : null,
      image: image || null,
      subtitle: subtitle ? subtitle.trim() : null
    });
  }

  /**
   * Update category with validation
   */
  static async updateCategory(id, updateData) {
    const { name, description, code, image, subtitle, status, position } = updateData;
    const updateFields = {};

    // Check if new category name conflicts with existing category
    if (name) {
      const existingCategory = await this.model.findOne({
        name: new RegExp(`^${name.trim()}$`, 'i'),
        _id: { $ne: id }
      });

      if (existingCategory) {
        throw new Error('Category name already exists');
      }
      updateFields.name = name.trim();
    }

    // Check if new code conflicts with existing category
    if (code !== undefined) {
      if (code) {
        const existingCode = await this.model.findOne({
          code: new RegExp(`^${code.trim()}$`, 'i'),
          _id: { $ne: id }
        });
        
        if (existingCode) {
          throw new Error('Category code already exists');
        }
        updateFields.code = code.trim();
      } else {
        updateFields.code = null;
      }
    }

    // Handle other fields
    if (description !== undefined) updateFields.description = description ? description.trim() : null;
    if (image !== undefined) updateFields.image = image || null;
    if (subtitle !== undefined) updateFields.subtitle = subtitle ? subtitle.trim() : null;
    if (status !== undefined) updateFields.status = status;

    // Handle position updates
    if (position !== undefined) {
      const newPosition = parseInt(position);
      
      // Get current category with its current position
      const currentCategory = await this.model.findById(id).select('position').lean();
      const currentPosition = currentCategory ? currentCategory.position : 0;
      
      if (currentPosition !== newPosition) {
        // If moving to a higher position (lower number), increment positions in between
        if (newPosition < currentPosition) {
          await this.model.updateMany(
            { 
              position: { $gte: newPosition, $lt: currentPosition },
              _id: { $ne: id }
            },
            { $inc: { position: 1 } }
          );
        } 
        // If moving to a lower position (higher number), decrement positions in between
        else if (newPosition > currentPosition) {
          await this.model.updateMany(
            { 
              position: { $gt: currentPosition, $lte: newPosition },
              _id: { $ne: id }
            },
            { $inc: { position: -1 } }
          );
        }
        
        // Update the position of the current category
        updateFields.position = newPosition;
      }
    }

    // Update the category with all fields including the new position if it was changed
    return this.model.findByIdAndUpdate(
      id, 
      updateFields, 
      { new: true, runValidators: true }
    );
  }

  /**
   * Get category statistics
   */
  static async getStatistics() {
    return this.aggregate([
      {
        $group: {
          _id: null,
          totalCategories: { $sum: 1 },
          activeCategories: {
            $sum: {
              $cond: [{ $eq: ['$status', true] }, 1, 0]
            }
          },
          inactiveCategories: {
            $sum: {
              $cond: [{ $eq: ['$status', false] }, 1, 0]
            }
          }
        }
      }
    ]).then(results => results[0] || {
      totalCategories: 0,
      activeCategories: 0,
      inactiveCategories: 0
    });
  }
}

module.exports = Category;
