import { DataManager } from "../base/DataManager.js";

export class AircallDataManager extends DataManager {
  constructor(mongodb, redis, timeManager) {
    const options = {
      collection: "aircall_daily_data",
      redisTTL: 300 // 5 minutes cache
    };
    super(mongodb, redis, timeManager, options);
    this.options = options;
  }

  ensureDate(d) {
    if (d instanceof Date) return d;
    if (typeof d === 'string') return new Date(d);
    if (typeof d === 'number') return new Date(d);
    console.error('Invalid date value:', d);
    return new Date(); // fallback to current date
  }

  async storeHistoricalPeriod(start, end, calls) {
    if (!this.mongodb) return;
  
    try {
      if (!Array.isArray(calls)) {
        console.error("Invalid calls data:", calls);
        return;
      }
  
      // Group calls by true day boundaries using TimeManager
      const dailyCallsMap = new Map();
  
      calls.forEach((call) => {
        try {
          const timestamp = call.started_at * 1000; // Convert to milliseconds
          const callDate = this.ensureDate(timestamp);
          const dayBounds = this.timeManager.getDayBounds(callDate);
          const dayKey = dayBounds.start.toISOString();
          
          if (!dailyCallsMap.has(dayKey)) {
            dailyCallsMap.set(dayKey, {
              date: dayBounds.start,
              calls: [],
            });
          }
          dailyCallsMap.get(dayKey).calls.push(call);
        } catch (err) {
          console.error('Error processing call:', err, call);
        }
      });
  
      // Iterate over each day in the period using day boundaries
      const dates = [];
      let currentDate = this.ensureDate(start);
      const endDate = this.ensureDate(end);
      
      while (currentDate < endDate) {
        const dayBounds = this.timeManager.getDayBounds(currentDate);
        dates.push(dayBounds.start);
        currentDate.setUTCDate(currentDate.getUTCDate() + 1);
      }
  
      for (const date of dates) {
        try {
          const dateKey = date.toISOString();
          const dayData = dailyCallsMap.get(dateKey);
          const dayCalls = dayData ? dayData.calls : [];
    
          // Process calls for this day using the same processing logic
          const metrics = this.processCallData(dayCalls);
    
          // Insert a daily_data record for this day
          metrics.daily_data = [
            {
              date: date.toISOString().split("T")[0],
              inbound: metrics.by_direction.inbound,
              outbound: metrics.by_direction.outbound,
            },
          ];
    
          // Store this day's processed data as historical
          await this.storeHistoricalDay(date, metrics);
        } catch (err) {
          console.error('Error processing date:', err, date);
        }
      }
  
    } catch (error) {
      console.error("Error storing historical period:", error, error.stack);
      throw error;
    }
  }

  processCallData(calls) {
    // If calls is already processed (has total, by_direction, etc.), return it
    if (calls && calls.total !== undefined) {
      console.log('Data already processed:', {
        total: calls.total,
        by_direction: calls.by_direction
      });
      // Return a clean copy of the processed data
      return {
        total: calls.total,
        by_direction: calls.by_direction,
        by_status: calls.by_status,
        by_missed_reason: calls.by_missed_reason,
        by_hour: calls.by_hour,
        by_users: calls.by_users,
        daily_data: calls.daily_data,
        duration_distribution: calls.duration_distribution,
        average_duration: calls.average_duration
      };
    }

    console.log('Processing raw calls:', {
      count: calls.length,
      sample: calls.length > 0 ? {
        id: calls[0].id,
        direction: calls[0].direction,
        status: calls[0].status
      } : null
    });

    // Process raw calls
    const metrics = {
      total: calls.length,
      by_direction: { inbound: 0, outbound: 0 },
      by_status: { answered: 0, missed: 0 },
      by_missed_reason: {},
      by_hour: Array(24).fill(0),
      by_users: {},
      daily_data: [],
      duration_distribution: [
        { range: "0-1m", count: 0 },
        { range: "1-5m", count: 0 },
        { range: "5-15m", count: 0 },
        { range: "15-30m", count: 0 },
        { range: "30m+", count: 0 },
      ],
      average_duration: 0,
      total_duration: 0,
    };

    // Group calls by date for daily data
    const dailyCallsMap = new Map();

    calls.forEach((call) => {
      try {
        // Direction metrics
        metrics.by_direction[call.direction]++;

        // Get call date and hour using TimeManager
        const timestamp = call.started_at * 1000; // Convert to milliseconds
        const callDate = this.ensureDate(timestamp);
        const dayBounds = this.timeManager.getDayBounds(callDate);
        const dayKey = dayBounds.start.toISOString().split("T")[0];
        const hour = callDate.getHours();
        metrics.by_hour[hour]++;

        // Status and duration metrics
        if (call.answered_at) {
          metrics.by_status.answered++;
          const duration = call.ended_at - call.answered_at;
          metrics.total_duration += duration;

          // Duration distribution
          if (duration <= 60) {
            metrics.duration_distribution[0].count++;
          } else if (duration <= 300) {
            metrics.duration_distribution[1].count++;
          } else if (duration <= 900) {
            metrics.duration_distribution[2].count++;
          } else if (duration <= 1800) {
            metrics.duration_distribution[3].count++;
          } else {
            metrics.duration_distribution[4].count++;
          }

          // Track user performance
          if (call.user) {
            const userId = call.user.id;
            if (!metrics.by_users[userId]) {
              metrics.by_users[userId] = {
                id: userId,
                name: call.user.name,
                total: 0,
                answered: 0,
                missed: 0,
                total_duration: 0,
                average_duration: 0,
              };
            }
            metrics.by_users[userId].total++;
            metrics.by_users[userId].answered++;
            metrics.by_users[userId].total_duration += duration;
          }
        } else {
          metrics.by_status.missed++;
          if (call.missed_call_reason) {
            metrics.by_missed_reason[call.missed_call_reason] =
              (metrics.by_missed_reason[call.missed_call_reason] || 0) + 1;
          }

          // Track missed calls by user
          if (call.user) {
            const userId = call.user.id;
            if (!metrics.by_users[userId]) {
              metrics.by_users[userId] = {
                id: userId,
                name: call.user.name,
                total: 0,
                answered: 0,
                missed: 0,
                total_duration: 0,
                average_duration: 0,
              };
            }
            metrics.by_users[userId].total++;
            metrics.by_users[userId].missed++;
          }
        }

        // Group by date for daily data
        if (!dailyCallsMap.has(dayKey)) {
          dailyCallsMap.set(dayKey, { date: dayKey, inbound: 0, outbound: 0 });
        }
        dailyCallsMap.get(dayKey)[call.direction]++;
      } catch (err) {
        console.error('Error processing call:', err, call);
      }
    });

    // Calculate average durations for users
    Object.values(metrics.by_users).forEach((user) => {
      if (user.answered > 0) {
        user.average_duration = Math.round(user.total_duration / user.answered);
      }
    });

    // Calculate global average duration
    if (metrics.by_status.answered > 0) {
      metrics.average_duration = Math.round(
        metrics.total_duration / metrics.by_status.answered
      );
    }

    // Convert daily data map to sorted array
    metrics.daily_data = Array.from(dailyCallsMap.values()).sort((a, b) =>
      a.date.localeCompare(b.date)
    );

    delete metrics.total_duration;

    console.log('Processed metrics:', {
      total: metrics.total,
      by_direction: metrics.by_direction,
      by_status: metrics.by_status,
      daily_data_count: metrics.daily_data.length
    });

    return metrics;
  }

  async storeHistoricalDay(date, data) {
    if (!this.mongodb) return;

    try {
      const collection = this.mongodb.collection(this.options.collection);
      const dayBounds = this.timeManager.getDayBounds(this.ensureDate(date));

      // Ensure consistent data structure with metrics nested in data field
      const document = {
        date: dayBounds.start,
        data: {
          total: data.total,
          by_direction: data.by_direction,
          by_status: data.by_status,
          by_missed_reason: data.by_missed_reason,
          by_hour: data.by_hour,
          by_users: data.by_users,
          daily_data: data.daily_data,
          duration_distribution: data.duration_distribution,
          average_duration: data.average_duration
        },
        updatedAt: new Date()
      };

      await collection.updateOne(
        { date: dayBounds.start },
        { $set: document },
        { upsert: true }
      );
    } catch (error) {
      console.error("Error storing historical day:", error);
      throw error;
    }
  }
}