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

// Initialize the schema first
// Function to generate a slug
const generateSlug = (name) => {
  if (!name) return `item-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
  return name
    .toString()
    .toLowerCase()
    .replace(/[^\w\s-]/g, '')
    .replace(/\s+/g, '-')
    .replace(/-+/g, '-')
    .trim()
    .substring(0, 50) + '-' + Math.random().toString(36).substring(2, 8);
};

const itemSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'Item name is required'],
    trim: true,
    maxlength: [100, 'Item name cannot exceed 100 characters']
  },
  brand_id: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Brand',
    required: true
  },
  category_id: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Category',
    required: true
  },
  unit_id: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Unit',
    required: true
  },
  sku: {
    type: String,
    required: true,
    maxlength: [100, 'SKU cannot exceed 100 characters']
  },
  hsn: {
    type: String,
    maxlength: [100, 'HSN cannot exceed 100 characters']
  },
  expire_date: {
    type: Date
  },
  barcode: {
    type: String,
    maxlength: [100, 'Barcode cannot exceed 100 characters']
  },
  description: {
    type: String,
    trim: true
  },
  images: [{
    url: {
      type: String,
      required: true,
      trim: true
    },
    is_primary: {
      type: Boolean,
      default: false
    },
    position: {
      type: Number,
      default: 0
    }
  }],
  price: {
    type: Number,
    required: true,
    min: [0, 'Price cannot be negative']
  },
  tax_id: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Tax'
  },
  tax_type: {
    type: String,
    enum: ['inclusive', 'exclusive'],
    default: 'exclusive'
  },
  sales_price: {
    type: Number,
    min: [0, 'Sales price cannot be negative']
  },
  discount_id: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Discount'
  },
  store_id: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Store',
    required: true
  },
  status: {
    type: Number,
    default: 1,
    enum: [0, 1], // 0 = Inactive, 1 = Active
    set: function(val) {
      // If status is being set to inactive, ensure stock is 0
      if (val === 0) {
        this.quantity = 0;
      }
      return val;
    }
  },
  requiresPrescription: {
    type: Boolean,
    default: false
  },
  quantity: {
    type: Number,
    default: 0,
    min: [0, 'Quantity cannot be negative'],
    required: [true, 'Quantity is required'],
    set: (val) => Math.max(0, Number(val) || 0)
  },
  min_stock_level: {
    type: Number,
    default: 10,
    min: [0, 'Minimum stock level cannot be negative']
  },
  max_stock_level: {
    type: Number,
    default: 100,
    min: [0, 'Maximum stock level cannot be negative'],
    validate: [
      {
        validator: function(value) {
          return this.min_stock_level <= value;
        },
        message: 'Maximum stock level must be greater than or equal to minimum stock level'
      }
    ]
  },
  last_stock_update: {
    type: Date,
    default: Date.now
  },
  stock_movements: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'StockMovement'
  }],
  slug: {
    type: String,
    required: true,
    unique: true,
    index: true
  }
}, {
  timestamps: true,
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
});

// Pre-validate hook to ensure slug is set before validation
itemSchema.pre('validate', function(next) {
  // Set a temporary slug if not set
  if (!this.slug) {
    this.slug = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 8)}`;
  }
  next();
});

// Pre-save hook to ensure slug is always set and unique
itemSchema.pre('save', async function(next) {
  if (!this.slug) {
    this.slug = generateSlug(this.name);
  }
  
  // Make sure the slug is unique
  const slugRegEx = new RegExp(`^(${this.slug})((-[0-9]*$)?)$`, 'i');
  const itemsWithSlug = await this.constructor.find({ slug: slugRegEx });
  
  if (itemsWithSlug.length) {
    this.slug = `${this.slug}-${Date.now()}`;
  }
  
  next();
});

// Virtual for brand
itemSchema.virtual('brand', {
  ref: 'Brand',
  localField: 'brand_id',
  foreignField: '_id',
  justOne: true
});

// Virtual for category
itemSchema.virtual('category', {
  ref: 'Category',
  localField: 'category_id',
  foreignField: '_id',
  justOne: true
});

// Virtual for unit
itemSchema.virtual('unit', {
  ref: 'Unit',
  localField: 'unit_id',
  foreignField: '_id',
  justOne: true
});

// Virtual for tax
itemSchema.virtual('tax', {
  ref: 'Tax',
  localField: 'tax_id',
  foreignField: '_id',
  justOne: true
});

// Virtual for discount
itemSchema.virtual('discount', {
  ref: 'Discount',
  localField: 'discount_id',
  foreignField: '_id',
  justOne: true
});

// Virtual for store
itemSchema.virtual('store', {
  ref: 'Store',
  localField: 'store_id',
  foreignField: '_id',
  justOne: true
});

// Indexes for better query performance
itemSchema.index({ name: 'text', description: 'text', sku: 'text', hsn: 'text', barcode: 'text' });
itemSchema.index({ brand_id: 1 });
itemSchema.index({ category_id: 1 });
itemSchema.index({ unit_id: 1 });
itemSchema.index({ tax_id: 1 });
itemSchema.index({ discount_id: 1 });
itemSchema.index({ store_id: 1 });
itemSchema.index({ status: 1 });
itemSchema.index({ price: 1 });
itemSchema.index({ sales_price: 1 });

class Item extends BaseModel {
  // Explicitly set the schema as a static property
  static schema = itemSchema;
  
  // Ensure the model is initialized
  static init() {
    if (this._model) return this._model;
    this._model = mongoose.models[this.name] || mongoose.model(this.name, this.schema);
    return this._model;
  }
  
  // Get the model instance
  static get model() {
    return this.init();
  }
  static init() {
    const model = super.init();
    
    // Create text index for search
    if (model.collection) {
      model.collection.createIndex({ 
        name: 'text', 
        description: 'text',
        sku: 'text'
      });
    }
    
    return model;
  }

  // Get item by ID with all details
  static async findByIdWithDetails(id) {
    return this.aggregate([
      {
        $match: {
          _id: new mongoose.Types.ObjectId(id),
          status: true
        }
      },
      {
        $lookup: {
          from: 'brands',
          localField: 'brand_id',
          foreignField: '_id',
          as: 'brand'
        }
      },
      {
        $lookup: {
          from: 'categories',
          localField: 'category_id',
          foreignField: '_id',
          as: 'category'
        }
      },
      {
        $lookup: {
          from: 'units',
          localField: 'unit_id',
          foreignField: '_id',
          as: 'unit'
        }
      },
      {
        $lookup: {
          from: 'taxes',
          localField: 'tax_id',
          foreignField: '_id',
          as: 'tax'
        }
      },
      {
        $lookup: {
          from: 'discounts',
          localField: 'discount_id',
          foreignField: '_id',
          as: 'discount'
        }
      },
      {
        $lookup: {
          from: 'stores',
          localField: 'store_id',
          foreignField: '_id',
          as: 'store'
        }
      },
      {
        $unwind: {
          path: '$brand',
          preserveNullAndEmptyArrays: true
        }
      },
      {
        $unwind: {
          path: '$category',
          preserveNullAndEmptyArrays: true
        }
      },
      {
        $unwind: {
          path: '$unit',
          preserveNullAndEmptyArrays: true
        }
      },
      {
        $unwind: {
          path: '$tax',
          preserveNullAndEmptyArrays: true
        }
      },
      {
        $unwind: {
          path: '$discount',
          preserveNullAndEmptyArrays: true
        }
      },
      {
        $unwind: {
          path: '$store',
          preserveNullAndEmptyArrays: true
        }
      },
      {
        $addFields: {
          id: '$_id',
          brand: {
            id: '$brand._id',
            name: '$brand.name',
            companyName: '$brand.companyName'
          },
          category: {
            id: '$category._id',
            name: '$category.name'
          },
          unit: {
            id: '$unit._id',
            name: '$unit.name',
            shortCode: '$unit.shortCode'
          },
          tax: {
            id: '$tax._id',
            name: '$tax.name',
            percentage: '$tax.percentage'
          },
          discount: {
            id: '$discount._id',
            name: '$discount.name',
            type: '$discount.type',
            value: '$discount.value'
          },
          store: {
            id: '$store._id',
            name: '$store.name',
            location: '$store.location'
          }
        }
      },
      {
        $project: {
          _id: 0,
          __v: 0,
          brand_id: 0,
          category_id: 0,
          unit_id: 0,
          tax_id: 0,
          discount_id: 0,
          store_id: 0,
          'brand._id': 0,
          'category._id': 0,
          'unit._id': 0,
          'tax._id': 0,
          'discount._id': 0,
          'store._id': 0
        }
      }
    ]).then(results => results[0] || null);
  }

  // Find items by category with pagination and sorting
  static async findByCategory(categoryId, options = {}) {
    const { 
      limit = 10, 
      offset = 0, 
      sortBy = 'createdAt', 
      sortOrder = 'desc',
      minPrice,
      maxPrice,
      brands = []
    } = options;

    const sort = {};
    sort[sortBy] = sortOrder === 'desc' ? -1 : 1;

    const match = {
      category_id: new mongoose.Types.ObjectId(categoryId),
      status: true
    };

    if (minPrice !== undefined || maxPrice !== undefined) {
      match.price = {};
      if (minPrice !== undefined) match.price.$gte = Number(minPrice);
      if (maxPrice !== undefined) match.price.$lte = Number(maxPrice);
    }

    if (brands.length > 0) {
      match.brand_id = { $in: brands.map(id => new mongoose.Types.ObjectId(id)) };
    }

    const pipeline = [
      { $match: match },
      {
        $lookup: {
          from: 'brands',
          localField: 'brand_id',
          foreignField: '_id',
          as: 'brand'
        }
      },
      { $unwind: '$brand' },
      {
        $lookup: {
          from: 'units',
          localField: 'unit_id',
          foreignField: '_id',
          as: 'unit'
        }
      },
      { $unwind: '$unit' },
      {
        $project: {
          _id: 0,
          id: '$_id',
          name: 1,
          description: 1,
          price: 1,
          sales_price: 1,
          image: 1,
          sku: 1,
          hsn: 1,
          minimum_qty: 1,
          status: 1,
          createdAt: 1,
          updatedAt: 1,
          brand: {
            id: '$brand._id',
            name: '$brand.name',
            image: '$brand.image'
          },
          unit: {
            id: '$unit._id',
            name: '$unit.name',
            shortCode: '$unit.shortCode'
          }
        }
      },
      { $sort: sort },
      { $skip: Number(offset) },
      { $limit: Number(limit) }
    ];

    const [items, total] = await Promise.all([
      this.aggregate(pipeline),
      this.countDocuments(match)
    ]);

    return {
      items,
      total,
      limit: Number(limit),
      offset: Number(offset)
    };
  }

  // Search items by query with pagination and sorting
  static async search(query, options = {}) {
    const { 
      limit = 10, 
      offset = 0, 
      sortBy = 'createdAt', 
      sortOrder = 'desc',
      minPrice,
      maxPrice,
      categories = [],
      brands = []
    } = options;

    const sort = {};
    sort[sortBy] = sortOrder === 'desc' ? -1 : 1;

    // Text search using $text index
    const textSearch = {
      $text: { $search: query },
      status: true
    };

    // Regular expression search as fallback
    const regexSearch = {
      $or: [
        { name: { $regex: query, $options: 'i' } },
        { description: { $regex: query, $options: 'i' } },
        { sku: { $regex: query, $options: 'i' } },
        { hsn: { $regex: query, $options: 'i' } },
        { barcode: { $regex: query, $options: 'i' } }
      ],
      status: true
    };

    // Determine which search to use based on query length
    const match = query.length > 3 ? textSearch : regexSearch;

    if (minPrice !== undefined || maxPrice !== undefined) {
      match.price = {};
      if (minPrice !== undefined) match.price.$gte = Number(minPrice);
      if (maxPrice !== undefined) match.price.$lte = Number(maxPrice);
    }

    if (categories.length > 0) {
      match.category_id = { $in: categories.map(id => new mongoose.Types.ObjectId(id)) };
    }

    if (brands.length > 0) {
      match.brand_id = { $in: brands.map(id => new mongoose.Types.ObjectId(id)) };
    }

    const pipeline = [
      { $match: match },
      {
        $lookup: {
          from: 'brands',
          localField: 'brand_id',
          foreignField: '_id',
          as: 'brand'
        }
      },
      { $unwind: '$brand' },
      {
        $lookup: {
          from: 'categories',
          localField: 'category_id',
          foreignField: '_id',
          as: 'category'
        }
      },
      { $unwind: '$category' },
      {
        $lookup: {
          from: 'units',
          localField: 'unit_id',
          foreignField: '_id',
          as: 'unit'
        }
      },
      { $unwind: '$unit' },
      {
        $project: {
          _id: 0,
          id: '$_id',
          name: 1,
          description: 1,
          price: 1,
          sales_price: 1,
          image: 1,
          sku: 1,
          hsn: 1,
          minimum_qty: 1,
          status: 1,
          createdAt: 1,
          updatedAt: 1,
          brand: {
            id: '$brand._id',
            name: '$brand.name',
            image: '$brand.image'
          },
          category: {
            id: '$category._id',
            name: '$category.name'
          },
          unit: {
            id: '$unit._id',
            name: '$unit.name',
            shortCode: '$unit.shortCode'
          },
          score: { $meta: 'textScore' }
        }
      },
      { 
        $sort: query.length > 3 
          ? { score: { $meta: 'textScore' }, ...sort } 
          : sort 
      },
      { $skip: Number(offset) },
      { $limit: Number(limit) }
    ];

    const [items, total] = await Promise.all([
      this.aggregate(pipeline),
      this.countDocuments(match)
    ]);

    return {
      items,
      total,
      limit: Number(limit),
      offset: Number(offset)
    };
  }
}

module.exports = Item;
