/**
 * Analytics Engine for Time Entries
 * Processes time entries data and generates insights, scores, and analytics
 */

import { TimeEntry } from '@/lib/types/database';
import { 
  ActivityScore, 
  ContentScore, 
  TimelinessScore, 
  AnalyticsInsight,
  TimeEntryAnalysis,
  AggregateAnalysis
} from '@/lib/types/analytics';

export class AnalyticsEngine {
  
  /**
   * Analyze a single time entry
   */
  analyzeTimeEntry(timeEntry: TimeEntry): TimeEntryAnalysis {
    const activityScore = this.calculateActivityScore(timeEntry);
    const contentScore = this.calculateContentScore(timeEntry);
    const timelinessScore = this.calculateTimelinessScore(timeEntry);
    
    // Calculate overall score (weighted average)
    const overallScore = (
      activityScore.score * 0.3 +
      contentScore.score * 0.4 +
      timelinessScore.score * 0.3
    );

    return {
      timeEntryId: timeEntry.id,
      activityScore,
      contentScore,
      timelinessScore,
      overallScore,
      insights: this.generateEntryInsights(timeEntry, activityScore, contentScore, timelinessScore),
      analyzedAt: new Date(),
    };
  }

  /**
   * Analyze multiple time entries and generate aggregate insights
   */
  analyzeTimeEntries(timeEntries: TimeEntry[]): AggregateAnalysis {
    if (timeEntries.length === 0) {
      return this.createEmptyAnalysis();
    }

    // Analyze individual entries
    const individualAnalyses = timeEntries.map(entry => this.analyzeTimeEntry(entry));

    // Calculate aggregate scores
    const avgActivityScore = this.calculateAverage(individualAnalyses.map(a => a.activityScore.score));
    const avgContentScore = this.calculateAverage(individualAnalyses.map(a => a.contentScore.score));
    const avgTimelinessScore = this.calculateAverage(individualAnalyses.map(a => a.timelinessScore.score));
    const avgOverallScore = this.calculateAverage(individualAnalyses.map(a => a.overallScore));

    // Generate insights
    const insights = this.generateAggregateInsights(timeEntries, individualAnalyses);

    // Calculate patterns and trends
    const patterns = this.analyzePatterns(timeEntries);
    const trends = this.analyzeTrends(timeEntries);

    return {
      totalEntries: timeEntries.length,
      totalHours: timeEntries.reduce((sum, entry) => sum + entry.hours_worked, 0),
      averageHoursPerEntry: this.calculateAverage(timeEntries.map(e => e.hours_worked)),
      dateRange: {
        earliest: new Date(Math.min(...timeEntries.map(e => new Date(e.entry_date).getTime()))),
        latest: new Date(Math.max(...timeEntries.map(e => new Date(e.entry_date).getTime()))),
      },
      scores: {
        activity: avgActivityScore,
        content: avgContentScore,
        timeliness: avgTimelinessScore,
        overall: avgOverallScore,
      },
      insights,
      patterns,
      trends,
      analyzedAt: new Date(),
    };
  }

  /**
   * Calculate Activity Score based on entry quality, completeness, and work patterns
   */
  private calculateActivityScore(timeEntry: TimeEntry): ActivityScore {
    let score = 0;
    const factors: string[] = [];

    // Factor 1: Entry completeness (40%)
    const completenessScore = this.calculateCompletenessScore(timeEntry);
    score += completenessScore * 0.4;
    if (completenessScore > 0.8) factors.push('Complete entry details');

    // Factor 2: Time tracking consistency (30%)
    const consistencyScore = this.calculateConsistencyScore(timeEntry);
    score += consistencyScore * 0.3;
    if (consistencyScore > 0.7) factors.push('Consistent time tracking');

    // Factor 3: Appropriate time duration (20%)
    const durationScore = this.calculateDurationScore(timeEntry);
    score += durationScore * 0.2;
    if (durationScore > 0.6) factors.push('Reasonable time duration');

    // Factor 4: Proper categorization (10%)
    const categorizationScore = this.calculateCategorizationScore(timeEntry);
    score += categorizationScore * 0.1;
    if (categorizationScore > 0.5) factors.push('Proper categorization');

    return {
      score: Math.round(score * 100) / 100,
      factors,
      breakdown: {
        completeness: completenessScore,
        consistency: consistencyScore,
        duration: durationScore,
        categorization: categorizationScore,
      },
    };
  }

  /**
   * Calculate Content Score based on time entry description quality and detail
   */
  private calculateContentScore(timeEntry: TimeEntry): ContentScore {
    let score = 0;
    const factors: string[] = [];

    // Factor 1: Notes quality and length (40%)
    const notesScore = this.calculateNotesQualityScore(timeEntry.notes);
    score += notesScore * 0.4;
    if (notesScore > 0.7) factors.push('Detailed work description');

    // Factor 2: Title clarity (20%)
    const titleScore = this.calculateTitleQualityScore(timeEntry.title);
    score += titleScore * 0.2;
    if (titleScore > 0.6) factors.push('Clear activity title');

    // Factor 3: Internal notes usage (20%)
    const internalNotesScore = this.calculateInternalNotesScore(timeEntry.internal_notes);
    score += internalNotesScore * 0.2;
    if (internalNotesScore > 0.5) factors.push('Internal documentation');

    // Factor 4: Technical detail level (20%)
    const technicalScore = this.calculateTechnicalDetailScore(timeEntry.notes, timeEntry.title);
    score += technicalScore * 0.2;
    if (technicalScore > 0.6) factors.push('Technical details included');

    return {
      score: Math.round(score * 100) / 100,
      factors,
      breakdown: {
        notesQuality: notesScore,
        titleClarity: titleScore,
        internalNotes: internalNotesScore,
        technicalDetail: technicalScore,
      },
    };
  }

  /**
   * Calculate Timeliness Score based on entry timing relative to work performed
   */
  private calculateTimelinessScore(timeEntry: TimeEntry): TimelinessScore {
    let score = 0;
    const factors: string[] = [];

    // Factor 1: Entry date vs work date (40%)
    const entryDelayScore = this.calculateEntryDelayScore(timeEntry);
    score += entryDelayScore * 0.4;
    if (entryDelayScore > 0.8) factors.push('Prompt time entry');

    // Factor 2: Business hours compliance (30%)
    const businessHoursScore = this.calculateBusinessHoursScore(timeEntry);
    score += businessHoursScore * 0.3;
    if (businessHoursScore > 0.7) factors.push('Business hours compliance');

    // Factor 3: Regularity pattern (20%)
    const regularityScore = this.calculateRegularityScore(timeEntry);
    score += regularityScore * 0.2;
    if (regularityScore > 0.6) factors.push('Regular entry pattern');

    // Factor 4: Approval time (10%)
    const approvalScore = this.calculateApprovalTimelinessScore(timeEntry);
    score += approvalScore * 0.1;
    if (approvalScore > 0.5) factors.push('Timely approval');

    return {
      score: Math.round(score * 100) / 100,
      factors,
      breakdown: {
        entryDelay: entryDelayScore,
        businessHours: businessHoursScore,
        regularity: regularityScore,
        approvalTimeliness: approvalScore,
      },
    };
  }

  /**
   * Helper methods for score calculations
   */
  private calculateCompletenessScore(timeEntry: TimeEntry): number {
    let score = 0;
    const totalFields = 8;
    
    if (timeEntry.title && timeEntry.title.length > 5) score++;
    if (timeEntry.notes && timeEntry.notes.length > 10) score++;
    if (timeEntry.start_date_time) score++;
    if (timeEntry.end_date_time) score++;
    if (timeEntry.ticket_id || timeEntry.task_id || timeEntry.project_id) score++;
    if (timeEntry.type !== undefined && timeEntry.type !== null) score++;
    if (timeEntry.billable !== undefined) score++;
    if (timeEntry.hours_worked > 0 && timeEntry.hours_worked <= 24) score++;

    return score / totalFields;
  }

  private calculateConsistencyScore(timeEntry: TimeEntry): number {
    // Check if start/end times match the hours worked
    if (timeEntry.start_date_time && timeEntry.end_date_time) {
      const start = new Date(timeEntry.start_date_time);
      const end = new Date(timeEntry.end_date_time);
      const actualHours = (end.getTime() - start.getTime()) / (1000 * 60 * 60);
      const difference = Math.abs(actualHours - timeEntry.hours_worked);
      
      // Score based on how close the logged hours match the actual time span
      if (difference < 0.1) return 1.0; // Very close
      if (difference < 0.5) return 0.8; // Close
      if (difference < 1.0) return 0.6; // Moderate
      if (difference < 2.0) return 0.4; // Poor
      return 0.2; // Very poor
    }
    
    // If no start/end times, score based on reasonable hour values
    if (timeEntry.hours_worked > 0 && timeEntry.hours_worked <= 12) return 0.7;
    if (timeEntry.hours_worked > 12 && timeEntry.hours_worked <= 24) return 0.5;
    return 0.3;
  }

  private calculateDurationScore(timeEntry: TimeEntry): number {
    const hours = timeEntry.hours_worked;
    
    // Optimal range: 0.25 to 8 hours
    if (hours >= 0.25 && hours <= 8) return 1.0;
    if (hours > 8 && hours <= 12) return 0.8;
    if (hours > 12 && hours <= 16) return 0.6;
    if (hours > 16 && hours <= 24) return 0.4;
    if (hours > 0 && hours < 0.25) return 0.5;
    return 0.2; // Invalid (0 or >24 hours)
  }

  private calculateCategorizationScore(timeEntry: TimeEntry): number {
    let score = 0;
    
    if (timeEntry.ticket_id) score += 0.4;
    if (timeEntry.task_id) score += 0.3;
    if (timeEntry.project_id) score += 0.2;
    if (timeEntry.type !== undefined && timeEntry.type !== null) score += 0.1;
    
    return Math.min(score, 1.0);
  }

  private calculateNotesQualityScore(notes?: string | null): number {
    if (!notes) return 0;
    
    let score = 0;
    
    // Length factor
    if (notes.length > 20) score += 0.3;
    if (notes.length > 50) score += 0.2;
    if (notes.length > 100) score += 0.2;
    
    // Content factors
    if (/\b(did|completed|fixed|resolved|created|updated|configured|installed|debugged|tested)\b/i.test(notes)) {
      score += 0.2;
    }
    
    if (/\b(because|due to|issue|problem|error|requirement|request)\b/i.test(notes)) {
      score += 0.1;
    }
    
    return Math.min(score, 1.0);
  }

  private calculateTitleQualityScore(title?: string | null): number {
    if (!title) return 0;
    
    let score = 0;
    
    if (title.length > 5) score += 0.4;
    if (title.length > 10) score += 0.3;
    if (title.length <= 50) score += 0.2; // Not too long
    
    // Contains action words
    if (/\b(work|task|activity|meeting|call|support|development|testing|documentation)\b/i.test(title)) {
      score += 0.1;
    }
    
    return Math.min(score, 1.0);
  }

  private calculateInternalNotesScore(internalNotes?: string | null): number {
    if (!internalNotes) return 0.5; // Neutral score if not used
    
    let score = 0.5; // Base score for using internal notes
    
    if (internalNotes.length > 20) score += 0.3;
    if (internalNotes.length > 50) score += 0.2;
    
    return Math.min(score, 1.0);
  }

  private calculateTechnicalDetailScore(notes?: string | null, title?: string | null): number {
    const text = `${notes || ''} ${title || ''}`.toLowerCase();
    
    let score = 0;
    
    // Technical keywords
    const technicalKeywords = [
      'api', 'database', 'server', 'code', 'script', 'config', ' firewall',
      'network', 'security', 'backup', 'restore', 'install', 'update',
      'debug', 'error', 'log', 'performance', 'optimization', 'migration'
    ];
    
    const keywordCount = technicalKeywords.filter(keyword => text.includes(keyword)).length;
    score = Math.min(keywordCount * 0.2, 1.0);
    
    return score;
  }

  private calculateEntryDelayScore(timeEntry: TimeEntry): number {
    const entryDate = new Date(timeEntry.entry_date);
    const createdDate = new Date(timeEntry.created_at);
    const delayDays = (createdDate.getTime() - entryDate.getTime()) / (1000 * 60 * 60 * 24);
    
    if (delayDays <= 1) return 1.0;
    if (delayDays <= 3) return 0.8;
    if (delayDays <= 7) return 0.6;
    if (delayDays <= 14) return 0.4;
    if (delayDays <= 30) return 0.2;
    return 0.1;
  }

  private calculateBusinessHoursScore(timeEntry: TimeEntry): number {
    if (!timeEntry.start_date_time) return 0.7; // Neutral if no start time
    
    const startTime = new Date(timeEntry.start_date_time);
    const hour = startTime.getHours();
    const dayOfWeek = startTime.getDay();
    
    // Weekday (Mon-Fri)
    if (dayOfWeek >= 1 && dayOfWeek <= 5) {
      // Business hours (8 AM - 6 PM)
      if (hour >= 8 && hour <= 18) return 1.0;
      // Early evening (6 PM - 9 PM)
      if (hour > 18 && hour <= 21) return 0.8;
      // Early morning (6 AM - 8 AM)
      if (hour >= 6 && hour < 8) return 0.7;
    }
    
    // Weekend
    if (dayOfWeek === 0 || dayOfWeek === 6) {
      return 0.5;
    }
    
    return 0.3; // Late night or unusual hours
  }

  private calculateRegularityScore(timeEntry: TimeEntry): number {
    // This would ideally compare with user's historical pattern
    // For now, return a neutral score
    return 0.7;
  }

  private calculateApprovalTimelinessScore(timeEntry: TimeEntry): number {
    if (!timeEntry.approved) return 0.5; // Neutral if not approved
    
    if (!timeEntry.approved_date_time) return 0.3;
    
    const entryDate = new Date(timeEntry.created_at);
    const approvalDate = new Date(timeEntry.approved_date_time);
    const approvalDelayDays = (approvalDate.getTime() - entryDate.getTime()) / (1000 * 60 * 60 * 24);
    
    if (approvalDelayDays <= 1) return 1.0;
    if (approvalDelayDays <= 3) return 0.8;
    if (approvalDelayDays <= 7) return 0.6;
    if (approvalDelayDays <= 14) return 0.4;
    return 0.2;
  }

  /**
   * Generate insights for individual time entry
   */
  private generateEntryInsights(
    timeEntry: TimeEntry,
    activityScore: ActivityScore,
    contentScore: ContentScore,
    timelinessScore: TimelinessScore
  ): AnalyticsInsight[] {
    const insights: AnalyticsInsight[] = [];
    
    // Low score insights
    if (activityScore.score < 0.5) {
      insights.push({
        type: 'warning',
        category: 'activity',
        title: 'Low Activity Score',
        description: 'This time entry has incomplete information or irregular patterns.',
        recommendation: 'Add more details to improve tracking accuracy.',
      });
    }
    
    if (contentScore.score < 0.5) {
      insights.push({
        type: 'warning',
        category: 'content',
        title: 'Poor Documentation',
        description: 'Work description lacks detail or clarity.',
        recommendation: 'Include specific tasks, outcomes, and technical details.',
      });
    }
    
    if (timelinessScore.score < 0.5) {
      insights.push({
        type: 'warning',
        category: 'timeliness',
        title: 'Delayed Entry',
        description: 'Time entry was logged significantly after work was performed.',
        recommendation: 'Try to enter time within 24 hours of completion.',
      });
    }
    
    // High score insights
    if (activityScore.score > 0.8 && contentScore.score > 0.8 && timelinessScore.score > 0.8) {
      insights.push({
        type: 'success',
        category: 'overall',
        title: 'Excellent Time Entry',
        description: 'This is a well-documented and timely time entry.',
        recommendation: 'Keep up the good work!',
      });
    }
    
    return insights;
  }

  /**
   * Generate aggregate insights for multiple time entries
   */
  private generateAggregateInsights(
    timeEntries: TimeEntry[],
    analyses: TimeEntryAnalysis[]
  ): AnalyticsInsight[] {
    const insights: AnalyticsInsight[] = [];
    
    // Overall performance insights
    const avgOverallScore = this.calculateAverage(analyses.map(a => a.overallScore));
    
    if (avgOverallScore > 0.8) {
      insights.push({
        type: 'success',
        category: 'overall',
        title: 'High Quality Time Tracking',
        description: 'Overall time entry quality is excellent.',
        recommendation: 'Maintain current documentation standards.',
      });
    } else if (avgOverallScore < 0.5) {
      insights.push({
        type: 'warning',
        category: 'overall',
        title: 'Poor Time Entry Quality',
        description: 'Time entries need improvement in documentation and timeliness.',
        recommendation: 'Provide training on proper time entry practices.',
      });
    }
    
    // Pattern insights
    const billableEntries = timeEntries.filter(e => e.billable).length;
    const billablePercentage = (billableEntries / timeEntries.length) * 100;
    
    if (billablePercentage < 50) {
      insights.push({
        type: 'info',
        category: 'billing',
        title: 'Low Billable Percentage',
        description: `Only ${billablePercentage.toFixed(1)}% of entries are marked as billable.`,
        recommendation: 'Review billing categorization to ensure accurate invoicing.',
      });
    }
    
    return insights;
  }

  /**
   * Analyze patterns in time entries
   */
  private analyzePatterns(timeEntries: TimeEntry[]) {
    // Group by day of week
    const dayOfWeekPattern = new Array(7).fill(0);
    timeEntries.forEach(entry => {
      const dayOfWeek = new Date(entry.entry_date).getDay();
      dayOfWeekPattern[dayOfWeek]++;
    });
    
    // Group by hour
    const hourlyPattern = new Array(24).fill(0);
    timeEntries.forEach(entry => {
      if (entry.start_date_time) {
        const hour = new Date(entry.start_date_time).getHours();
        hourlyPattern[hour]++;
      }
    });
    
    return {
      dayOfWeek: dayOfWeekPattern,
      hourly: hourlyPattern,
    };
  }

  /**
   * Analyze trends in time entries
   */
  private analyzeTrends(timeEntries: TimeEntry[]) {
    // Sort by date
    const sortedEntries = [...timeEntries].sort((a, b) => 
      new Date(a.entry_date).getTime() - new Date(b.entry_date).getTime()
    );
    
    // Calculate weekly trends
    const weeklyTrends: { week: Date; hours: number; entries: number }[] = [];
    const weeklyMap = new Map<string, { hours: number; entries: number }>();
    
    sortedEntries.forEach(entry => {
      const date = new Date(entry.entry_date);
      const weekStart = new Date(date.setDate(date.getDate() - date.getDay()));
      const weekKey = weekStart.toISOString().split('T')[0];
      
      if (!weeklyMap.has(weekKey)) {
        weeklyMap.set(weekKey, { hours: 0, entries: 0 });
      }
      
      const week = weeklyMap.get(weekKey)!;
      week.hours += entry.hours_worked;
      week.entries++;
    });
    
    weeklyMap.forEach((data, weekKey) => {
      weeklyTrends.push({
        week: new Date(weekKey),
        hours: data.hours,
        entries: data.entries,
      });
    });
    
    return {
      weekly: weeklyTrends.sort((a, b) => a.week.getTime() - b.week.getTime()),
    };
  }

  /**
   * Utility methods
   */
  private calculateAverage(values: number[]): number {
    if (values.length === 0) return 0;
    return values.reduce((sum, value) => sum + value, 0) / values.length;
  }

  private createEmptyAnalysis(): AggregateAnalysis {
    return {
      totalEntries: 0,
      totalHours: 0,
      averageHoursPerEntry: 0,
      dateRange: {
        earliest: new Date(),
        latest: new Date(),
      },
      scores: {
        activity: 0,
        content: 0,
        timeliness: 0,
        overall: 0,
      },
      insights: [],
      patterns: {
        dayOfWeek: new Array(7).fill(0),
        hourly: new Array(24).fill(0),
      },
      trends: {
        weekly: [],
      },
      analyzedAt: new Date(),
    };
  }
}

// Create singleton instance
export const analyticsEngine = new AnalyticsEngine();
