const couchdbService = require("../services/couchdbService");
const { 
  ValidationError,
  NotFoundError, 
  DatabaseError, 
  withErrorHandling,
  createErrorContext
} = require("../utils/modelErrors");

class Reward {
  constructor(data) {
    // Handle both new documents and database documents
    const isNew = !data._id;
    
    // For new documents, validate required fields
    if (isNew) {
      if (!data.name || data.name.trim() === '') {
        throw new ValidationError('Name is required', 'name', data.name);
      }
      if (!data.description || data.description.trim() === '') {
        throw new ValidationError('Description is required', 'description', data.description);
      }
      if (data.cost === undefined || data.cost === null || isNaN(data.cost)) {
        throw new ValidationError('Cost is required and must be a number', 'cost', data.cost);
      }
      if (data.cost < 0) {
        throw new ValidationError('Cost must be non-negative', 'cost', data.cost);
      }
    }

    // Assign properties
    this._id = data._id || null;
    this._rev = data._rev || null;
    this.type = data.type || "reward";
    this.name = data.name;
    this.description = data.description;
    this.cost = Number(data.cost);
    this.category = data.category || null;
    this.isPremium = Boolean(data.isPremium);
    this.isActive = data.isActive !== undefined ? Boolean(data.isActive) : true;
    this.redeemedBy = data.redeemedBy || [];
    this.createdAt = data.createdAt || new Date().toISOString();
    this.updatedAt = data.updatedAt || new Date().toISOString();
  }

  toJSON() {
    return {
      _id: this._id,
      _rev: this._rev,
      type: this.type,
      name: this.name,
      description: this.description,
      cost: this.cost,
      category: this.category,
      isPremium: this.isPremium,
      isActive: this.isActive,
      redeemedBy: this.redeemedBy,
      createdAt: this.createdAt,
      updatedAt: this.updatedAt
    };
  }

  async save() {
    const errorContext = createErrorContext('Reward', 'save', { 
      rewardId: this._id,
      name: this.name 
    });
    
    return await withErrorHandling(async () => {
      if (this._id) {
        // Update existing document
        const updatedDoc = await couchdbService.updateDocument(this._id, this.toJSON());
        Object.assign(this, updatedDoc);
        return this;
      } else {
        // Create new document
        this._id = `reward_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        const createdDoc = await couchdbService.createDocument(this.toJSON());
        Object.assign(this, createdDoc);
        return this;
      }
    }, errorContext);
  }

  static async create(rewardData) {
    const errorContext = createErrorContext('Reward', 'create', { 
      name: rewardData?.name,
      cost: rewardData?.cost
    });
    
    return await withErrorHandling(async () => {
      const reward = new Reward(rewardData);
      reward._id = `reward_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
      
      const createdReward = await couchdbService.createDocument(reward.toJSON());
      return new Reward(createdReward);
    }, errorContext);
  }

  static async findById(rewardId) {
    const errorContext = createErrorContext('Reward', 'findById', { rewardId });
    
    return await withErrorHandling(async () => {
      const doc = await couchdbService.getById(rewardId);
      if (doc && doc.type === "reward") {
        return new Reward(doc);
      }
      return null;
    }, errorContext);
  }

  static async find(query = {}, options = {}) {
    const errorContext = createErrorContext('Reward', 'find', { query, options });
    
    return await withErrorHandling(async () => {
      const defaultQuery = {
        type: "reward",
        ...query
      };

      const docs = await couchdbService.find({
        selector: defaultQuery,
        ...options
      });
      
      return docs.map(doc => new Reward(doc));
    }, errorContext);
  }

  static async findOne(query) {
    const errorContext = createErrorContext('Reward', 'findOne', { query });
    
    return await withErrorHandling(async () => {
      const rewards = await this.find(query, { limit: 1 });
      return rewards[0] || null;
    }, errorContext);
  }

  static async update(rewardId, updateData) {
    const errorContext = createErrorContext('Reward', 'update', { rewardId, updateData });
    
    return await withErrorHandling(async () => {
      const existingReward = await this.findById(rewardId);
      if (!existingReward) {
        throw new NotFoundError('Reward', rewardId);
      }

      // Update fields
      Object.keys(updateData).forEach(key => {
        if (key !== '_id' && key !== '_rev' && key !== 'type') {
          existingReward[key] = updateData[key];
        }
      });
      
      existingReward.updatedAt = new Date().toISOString();
      
      const updatedDoc = await couchdbService.updateDocument(rewardId, existingReward.toJSON());
      return new Reward(updatedDoc);
    }, errorContext);
  }

  static async delete(rewardId) {
    const errorContext = createErrorContext('Reward', 'delete', { rewardId });
    
    return await withErrorHandling(async () => {
      const reward = await this.findById(rewardId);
      if (!reward) {
        throw new NotFoundError('Reward', rewardId);
      }
      
      return await couchdbService.deleteDocument(rewardId);
    }, errorContext);
  }

  static async findByCostRange(minCost, maxCost) {
    const errorContext = createErrorContext('Reward', 'findByCostRange', { minCost, maxCost });
    
    return await withErrorHandling(async () => {
      const docs = await couchdbService.find({
        selector: {
          type: "reward",
          cost: { $gte: minCost, $lte: maxCost }
        },
        sort: [{ cost: "asc" }]
      });
      
      return docs.map(doc => new Reward(doc));
    }, errorContext);
  }

  static async findByPremiumStatus(isPremium) {
    const errorContext = createErrorContext('Reward', 'findByPremiumStatus', { isPremium });
    
    return await withErrorHandling(async () => {
      return await this.find({ isPremium });
    }, errorContext);
  }

  static async getActiveRewards() {
    const errorContext = createErrorContext('Reward', 'getActiveRewards', {});
    
    return await withErrorHandling(async () => {
      return await this.find({ isActive: true });
    }, errorContext);
  }

  static async getPremiumRewards() {
    const errorContext = createErrorContext('Reward', 'getPremiumRewards', {});
    
    return await withErrorHandling(async () => {
      return await this.find({ isPremium: true, isActive: true });
    }, errorContext);
  }

  static async getRegularRewards() {
    const errorContext = createErrorContext('Reward', 'getRegularRewards', {});
    
    return await withErrorHandling(async () => {
      return await this.find({ isPremium: false, isActive: true });
    }, errorContext);
  }

  static async getAllPaginated(page = 1, limit = 10) {
    const errorContext = createErrorContext('Reward', 'getAllPaginated', { page, limit });
    
    return await withErrorHandling(async () => {
      const skip = (page - 1) * limit;
      
      const rewards = await couchdbService.find({
        selector: { type: "reward" },
        sort: [{ cost: "asc" }],
        skip,
        limit
      });

      // Get total count
      const totalCount = await couchdbService.find({
        selector: { type: "reward" },
        fields: ["_id"]
      });

      return {
        rewards: rewards.map(doc => new Reward(doc)),
        pagination: {
          page,
          limit,
          totalCount: totalCount.length,
          totalPages: Math.ceil(totalCount.length / limit)
        }
      };
    }, errorContext);
  }

  static async redeemReward(userId, rewardId) {
    const errorContext = createErrorContext('Reward', 'redeemReward', { userId, rewardId });
    
    return await withErrorHandling(async () => {
      const reward = await this.findById(rewardId);
      if (!reward) {
        throw new NotFoundError('Reward', rewardId);
      }

      if (!reward.isActive) {
        throw new ValidationError('Reward is not available', 'isActive', reward.isActive);
      }

      const user = await couchdbService.findUserById(userId);
      if (!user) {
        throw new NotFoundError('User', userId);
      }

      if (user.points < reward.cost) {
        throw new ValidationError('Not enough points to redeem this reward', 'points', user.points);
      }

      if (reward.isPremium && !user.isPremium) {
        throw new ValidationError('Premium reward not available for non-premium users', 'isPremium', user.isPremium);
      }

      // Deduct points using couchdbService method
      const updatedUser = await couchdbService.updateUserPoints(
        userId,
        -reward.cost,
        `Redeemed reward: ${reward.name}`,
        {
          entityType: 'Reward',
          entityId: rewardId,
          entityName: reward.name
        }
      );

      // Create redemption record
      const redemption = {
        _id: `redemption_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        type: "reward_redemption",
        user: {
          userId: userId,
          name: user.name
        },
        reward: {
          rewardId: rewardId,
          name: reward.name,
          description: reward.description,
          cost: reward.cost
        },
        pointsDeducted: reward.cost,
        balanceAfter: updatedUser.points,
        redeemedAt: new Date().toISOString()
      };

      await couchdbService.create(redemption);

      return {
        redemption,
        pointsDeducted: reward.cost,
        newBalance: updatedUser.points
      };
    }, errorContext);
  }

  static async getUserRedemptions(userId, limit = 20) {
    const errorContext = createErrorContext('Reward', 'getUserRedemptions', { userId, limit });
    
    return await withErrorHandling(async () => {
      return await couchdbService.find({
        selector: {
          type: "reward_redemption",
          "user.userId": userId
        },
        sort: [{ redeemedAt: "desc" }],
        limit
      });
    }, errorContext);
  }

  static async getRewardStats(rewardId) {
    const errorContext = createErrorContext('Reward', 'getRewardStats', { rewardId });
    
    return await withErrorHandling(async () => {
      const redemptions = await couchdbService.find({
        selector: {
          type: "reward_redemption",
          "reward.rewardId": rewardId
        }
      });

      return {
        totalRedemptions: redemptions.length,
        totalPointsSpent: redemptions.reduce((sum, r) => sum + r.pointsDeducted, 0),
        lastRedeemed: redemptions.length > 0 ? redemptions[0].redeemedAt : null
      };
    }, errorContext);
  }

  static async getCatalogStats() {
    const errorContext = createErrorContext('Reward', 'getCatalogStats', {});
    
    return await withErrorHandling(async () => {
      const rewards = await this.getActiveRewards();
      const premium = await this.getPremiumRewards();
      const regular = await this.getRegularRewards();

      return {
        totalRewards: rewards.length,
        premiumRewards: premium.length,
        regularRewards: regular.length,
        averageCost: rewards.reduce((sum, r) => sum + r.cost, 0) / rewards.length || 0,
        minCost: rewards.length > 0 ? Math.min(...rewards.map(r => r.cost)) : 0,
        maxCost: rewards.length > 0 ? Math.max(...rewards.map(r => r.cost)) : 0
      };
    }, errorContext);
  }

  static async searchRewards(searchTerm, options = {}) {
    const errorContext = createErrorContext('Reward', 'searchRewards', { searchTerm, options });
    
    return await withErrorHandling(async () => {
      const query = {
        selector: {
          type: "reward",
          isActive: true,
          $or: [
            { name: { $regex: searchTerm, $options: "i" } },
            { description: { $regex: searchTerm, $options: "i" } }
          ]
        },
        ...options
      };

      const docs = await couchdbService.find(query);
      return docs.map(doc => new Reward(doc));
    }, errorContext);
  }

  // Migration helper
  static async migrateFromMongo(mongoReward) {
    const errorContext = createErrorContext('Reward', 'migrateFromMongo', { 
      mongoRewardId: mongoReward._id 
    });
    
    return await withErrorHandling(async () => {
      const rewardData = {
        name: mongoReward.name,
        description: mongoReward.description,
        cost: mongoReward.cost,
        isPremium: mongoReward.isPremium || false,
        isActive: true
      };

      return await this.create(rewardData);
    }, errorContext);
  }

  // Bulk operations for admin
  static async bulkCreate(rewardsData) {
    const errorContext = createErrorContext('Reward', 'bulkCreate', { 
      count: rewardsData.length 
    });
    
    return await withErrorHandling(async () => {
      const rewards = rewardsData.map(data => ({
        _id: `reward_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        type: "reward",
        name: data.name,
        description: data.description,
        cost: data.cost,
        isPremium: data.isPremium || false,
        isActive: data.isActive !== undefined ? data.isActive : true,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      }));

      const docs = await couchdbService.bulkDocs(rewards);
      return docs.map(doc => new Reward(doc));
    }, errorContext);
  }

  static async toggleActiveStatus(rewardId) {
    const errorContext = createErrorContext('Reward', 'toggleActiveStatus', { rewardId });
    
    return await withErrorHandling(async () => {
      const reward = await this.findById(rewardId);
      if (!reward) {
        throw new NotFoundError('Reward', rewardId);
      }

      return await this.update(rewardId, { isActive: !reward.isActive });
    }, errorContext);
  }
}

module.exports = Reward;