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

const brandSchema = new mongoose.Schema({
  brandName: {
    type: String,
    required: [true, 'Brand name is required'],
    trim: true,
    maxlength: [100, 'Brand name cannot exceed 100 characters'],
    minlength: [1, 'Brand name cannot be empty']
  },
  companyName: {
    type: String,
    trim: true,
    maxlength: [100, 'Company name cannot exceed 100 characters'],
    default: null
  },
  images: {
    type: String,
    trim: true,
    default: null,
    // Can store JSON string of image URLs or single image path
    validate: {
      validator: function(v) {
        if (!v) return true; // Allow null/empty
        try {
          // Check if it's valid JSON or a simple string
          JSON.parse(v);
          return true;
        } catch {
          // Allow simple strings (file paths, URLs)
          return typeof v === 'string' && v.length > 0;
        }
      },
      message: 'Images must be a valid JSON string or file path'
    }
  },
  description: {
    type: String,
    trim: true,
    maxlength: [1000, 'Description cannot exceed 1000 characters'],
    default: null
  },
  status: {
    type: Boolean,
    default: true
  },
  slug: {
    type: String,
    required: true,
    unique: true,
    trim: true,
    lowercase: true,
    maxlength: [150, 'Slug cannot exceed 150 characters']
  }
}, {
  timestamps: true, // This adds createdAt and updatedAt
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
});

// Indexes for better performance
brandSchema.index({ brandName: 1 });
brandSchema.index({ companyName: 1 });
brandSchema.index({ status: 1 });
brandSchema.index({ brandName: 'text', description: 'text', companyName: 'text' }); // Text search

// Virtual for getting items associated with this brand
brandSchema.virtual('items', {
  ref: 'Item',
  localField: '_id',
  foreignField: 'brandId'
});

// Function to generate a URL-friendly slug from a string
const generateSlug = (str) => {
  if (!str) return `brand-${Date.now()}-${Math.random().toString(36).substr(2, 8)}`;
  return str
    .toString()
    .toLowerCase()
    .replace(/[^\w\s-]/g, '') // Remove special characters
    .trim()
    .replace(/\s+/g, '-') // Replace spaces with hyphens
    .replace(/--+/g, '-') // Replace multiple hyphens with single hyphen
    .substring(0, 150); // Limit length
};

// Pre-validate hook to ensure slug is set before validation
brandSchema.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 middleware to ensure data consistency and generate slug
brandSchema.pre('save', async function(next) {
  // Trim whitespace from string fields
  if (this.brandName) this.brandName = this.brandName.trim();
  if (this.companyName) this.companyName = this.companyName.trim();
  if (this.description) this.description = this.description.trim();

  // Generate slug from brandName if it's a new document or brandName was modified
  if (this.isNew || this.isModified('brandName')) {
    this.slug = generateSlug(this.brandName);
    
    // Make sure the slug is unique
    if (this.slug) {
      const slugRegEx = new RegExp(`^(${this.slug})((-[0-9]*$)?)$`, 'i');
      const brandsWithSlug = await this.constructor.find({ slug: slugRegEx, _id: { $ne: this._id } });
      
      if (brandsWithSlug.length > 0) {
        this.slug = `${this.slug}-${Date.now()}`;
      }
    }
  }

  next();
});

// Static method to find active brands
brandSchema.statics.findActive = function() {
  return this.find({ status: true });
};

// Static method to find brands by company
brandSchema.statics.findByCompany = function(companyName) {
  return this.find({
    companyName: new RegExp(companyName, 'i'),
    status: true
  });
};

// Static method to find brand by slug
brandSchema.statics.findBySlug = function(slug) {
  return this.findOne({ slug, status: true });
};

// Static method to search brands
brandSchema.statics.search = function(query) {
  return this.find(
    {
      $text: { $search: query },
      status: true
    },
    { score: { $meta: 'textScore' } }
  ).sort({ score: { $meta: 'textScore' } });
};

// Instance method to get brand info
brandSchema.methods.getBasicInfo = function() {
  return {
    id: this._id,
    brandName: this.brandName,
    companyName: this.companyName,
    description: this.description,
    status: this.status,
    createdAt: this.createdAt,
    updatedAt: this.updatedAt
  };
};

// Instance method to toggle status
brandSchema.methods.toggleStatus = function() {
  this.status = !this.status;
  return this.save();
};

class Brand extends BaseModel {
  static get schema() {
    return brandSchema;
  }

  /**
   * Initialize the Brand model
   */
  static init() {
    const model = super.init();

    // Ensure text index is created - drop existing conflicting index first
    if (model.collection) {
      model.collection.dropIndex('name_text_description_text').catch(() => {
        // Ignore error if index doesn't exist
      }).finally(() => {
        model.collection.createIndex({
          brandName: 'text',
          description: 'text',
          companyName: 'text'
        }).catch(err => {
          console.warn('Failed to create text index for Brand:', err.message);
        });
      });
    }

    return model;
  }

  /**
   * Get all brands with pagination
   */
  static async findAllActive(options = {}) {
    const { page = 1, limit = 10, search, company } = options;

    let query = {};

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

    // Add company filter
    if (company) {
      query.companyName = new RegExp(company, 'i');
    }

    return this.paginate(query, {
      page,
      limit,
      sort: search ? { score: { $meta: 'textScore' } } : { createdAt: -1 },
      select: 'brandName companyName description images status createdAt updatedAt',
      lean: true
    });
  }

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

  /**
   * Get brands with item counts
   */
  static async findWithItemCounts(options = {}) {
    const { limit = 20 } = options;

    return this.aggregate([
      {
        $lookup: {
          from: 'items',
          let: { brandId: '$_id' },
          pipeline: [
            {
              $match: {
                $expr: { $eq: ['$brandId', '$$brandId'] }
              }
            },
            {
              $count: 'count'
            }
          ],
          as: 'itemCount'
        }
      },
      {
        $addFields: {
          items_count: { $arrayElemAt: ['$itemCount.count', 0] },
          id: '$_id'
        }
      },
      {
        $project: {
          itemCount: 0
        }
      },
      {
        $sort: { items_count: -1, brandName: 1 }
      },
      {
        $limit: limit
      }
    ]);
  }

  /**
   * Create new brand with validation
   */
  static async createBrand(data) {
    const { brandName, companyName, description, images } = data;

    // Check if brand name already exists (including deleted ones)
    const existingBrand = await this.findOne({
      brandName: new RegExp(`^${brandName.trim()}$`, 'i')
    });

    if (existingBrand) {
      throw new Error('Brand name already exists');
    }

    // Create and return the brand document
    return await this.create({
      brandName: brandName.trim(),
      companyName: companyName ? companyName.trim() : null,
      description: description ? description.trim() : null,
      images: images,
      slug: generateSlug(brandName) // Initial slug, will be made unique in pre-save
    });
  }

  /**
   * Update brand with validation
   */
  static async updateBrand(id, updateData) {
    const { brandName, companyName, description, images, status } = updateData;

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

      if (existingBrand) {
        throw new Error('Brand name already exists');
      }
    }

    const updateFields = {};
    if (brandName !== undefined) updateFields.brandName = brandName.trim();
    if (companyName !== undefined) updateFields.companyName = companyName ? companyName.trim() : null;
    if (description !== undefined) updateFields.description = description ? description.trim() : null;
    if (images !== undefined) updateFields.images = images;
    if (status !== undefined) updateFields.status = status;

    // Note: slug will be auto-updated by pre-save middleware if brandName changes
    return this.update(id, updateFields);
  }

  /**
   * Permanently delete brand
   */
  static async deleteBrand(id) {
    return this.delete(id);
  }

  /**
   * Get brand statistics
   */
  static async getStatistics() {
    return this.aggregate([
      {
        $group: {
          _id: null,
          totalBrands: { $sum: 1 },
          brandsWithItems: {
            $sum: {
              $cond: [
                { $gt: [{ $size: '$items' }, 0] },
                1,
                0
              ]
            }
          }
        }
      }
    ]).then(results => results[0] || {
      totalBrands: 0,
      brandsWithItems: 0
    });
  }
}

module.exports = Brand;
