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

const unitSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'Unit name is required'],
    trim: true,
    maxlength: [50, 'Unit name cannot exceed 50 characters']
  },
  short_code: {
    type: String,
    trim: true,
    maxlength: [10, 'Short code cannot exceed 10 characters'],
    default: null
  },
  remark: {
    type: String,
    trim: true,
    default: null
  },
  status: {
    type: Boolean,
    default: true
  }
}, {
  timestamps: true,
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
});

// Indexes for better performance
unitSchema.index({ name: 1 });
unitSchema.index({ short_code: 1 });
unitSchema.index({ status: 1 });
unitSchema.index({ name: 'text', remark: 'text' }); // Text search

// Virtual for getting items using this unit
unitSchema.virtual('items', {
  ref: 'Item',
  localField: '_id',
  foreignField: 'unitId'
});

class Unit extends BaseModel {
  static get schema() {
    return unitSchema;
  }

  /**
   * Initialize the Unit model
   */
  static init() {
    const model = super.init();
    
    // Ensure text index is created
    if (model.collection) {
      model.collection.createIndex({
        name: 'text',
        remark: 'text'
      }).catch(err => {
        console.warn('Failed to create text index for Unit:', err.message);
      });
    }
    
    return model;
  }

  /**
   * Get all units with pagination and search
   */
  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' } } : { createdAt: -1 },
      select: 'name short_code remark status createdAt updatedAt',
      lean: true
    });
  }

  /**
   * Get unit by ID with associated items count
   */
  static async findByIdWithDetails(id) {
    return this.aggregate([
      {
        $match: {
          _id: new mongoose.Types.ObjectId(id)
        }
      },
      {
        $lookup: {
          from: 'items',
          let: { unitId: '$_id' },
          pipeline: [
            {
              $match: {
                $expr: {
                  $and: [
                    { $eq: ['$unitId', '$$unitId'] },
                    { $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 unit with validation
   */
  static async createUnit(data) {
    const { name, short_code, remark } = data;

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

    if (existingUnit) {
      throw new Error('Unit name already exists');
    }

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

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

    return this.create({
      name: name.trim(),
      short_code: short_code ? short_code.trim() : null,
      remark: remark ? remark.trim() : null
    });
  }

  /**
   * Update unit with validation
   */
  static async updateUnit(id, updateData) {
    const { name, short_code, remark, status } = updateData;

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

      if (existingUnit) {
        throw new Error('Unit name already exists');
      }
    }

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

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

    const updateFields = {};
    if (name !== undefined) updateFields.name = name.trim();
    if (short_code !== undefined) updateFields.short_code = short_code ? short_code.trim() : null;
    if (remark !== undefined) updateFields.remark = remark ? remark.trim() : null;
    if (status !== undefined) updateFields.status = status;

    return this.update(id, updateFields);
  }

  /**
   * Permanently delete unit (hard delete)
   */
  static async deleteUnit(id) {
    return this.delete(id);
  }

  /**
   * Get active units for dropdowns
   */
  static async getActiveUnits() {
    return this.find(
      { status: true },
      'name short_code',
      { sort: { name: 1 } }
    );
  }

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

  /**
   * Get unit map for quick lookups
   */
  static async getUnitMap() {
    const units = await this.getActiveUnits();
    const unitMap = {};
    units.forEach(unit => {
      unitMap[unit._id.toString()] = unit;
    });
    return unitMap;
  }
}

module.exports = Unit;
