import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Between } from 'typeorm';
import {
  Activity,
  ActivityType,
} from '../../database/entities/activity.entity';
import { Child } from '../../database/entities/child.entity';
import {
  PatternAnalysisService,
  PatternInsights,
} from './pattern-analysis.service';
import { PredictionService, PredictionInsights } from './prediction.service';
import * as PDFDocument from 'pdfkit';

export interface WeeklyReport {
  childId: string;
  childName: string;
  weekStart: Date;
  weekEnd: Date;
  summary: {
    totalSleep: number; // minutes
    totalFeedings: number;
    totalDiaperChanges: number;
    averageSleepPerNight: number; // minutes
    averageNapsPerDay: number;
  };
  patterns: PatternInsights;
  predictions: PredictionInsights;
  highlights: string[];
  concerns: string[];
}

export interface MonthlyReport {
  childId: string;
  childName: string;
  month: string; // YYYY-MM format
  summary: {
    totalSleep: number;
    totalFeedings: number;
    totalDiaperChanges: number;
    weeklyAverages: {
      sleep: number;
      feedings: number;
      diapers: number;
    };
  };
  trends: {
    sleepTrend: 'improving' | 'stable' | 'declining';
    feedingTrend: 'increasing' | 'stable' | 'decreasing';
    growthMilestones: number;
  };
  weeklyBreakdown: WeeklyReport[];
}

export interface ExportData {
  format: 'json' | 'csv' | 'pdf';
  data: any;
  generatedAt: Date;
  contentType?: string;
}

@Injectable()
export class ReportService {
  private readonly logger = new Logger('ReportService');

  constructor(
    @InjectRepository(Activity)
    private activityRepository: Repository<Activity>,
    @InjectRepository(Child)
    private childRepository: Repository<Child>,
    private patternAnalysisService: PatternAnalysisService,
    private predictionService: PredictionService,
  ) {}

  /**
   * Generate weekly report for a child
   */
  async generateWeeklyReport(
    childId: string,
    startDate: Date | null = null,
  ): Promise<WeeklyReport> {
    const child = await this.childRepository.findOne({
      where: { id: childId },
    });
    if (!child) {
      throw new Error('Child not found');
    }

    // Default to last 7 days if no start date provided
    const weekStart =
      startDate || new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
    const weekEnd = new Date(weekStart.getTime() + 7 * 24 * 60 * 60 * 1000);

    // Fetch activities for the week
    const activities = await this.activityRepository.find({
      where: {
        childId,
        startedAt: Between(weekStart, weekEnd),
      },
      order: { startedAt: 'ASC' },
    });

    // Calculate summary statistics
    const summary = this.calculateWeeklySummary(activities);

    // Get patterns and predictions
    const patterns = await this.patternAnalysisService.analyzePatterns(
      childId,
      7,
    );
    const predictions =
      await this.predictionService.generatePredictions(childId);

    // Generate highlights and concerns
    const highlights = this.generateHighlights(summary, patterns);
    const concerns = patterns.concernsDetected;

    return {
      childId,
      childName: child.name,
      weekStart,
      weekEnd,
      summary,
      patterns,
      predictions,
      highlights,
      concerns,
    };
  }

  /**
   * Generate monthly report for a child
   */
  async generateMonthlyReport(
    childId: string,
    monthDate: Date | null = null,
  ): Promise<MonthlyReport> {
    const child = await this.childRepository.findOne({
      where: { id: childId },
    });
    if (!child) {
      throw new Error('Child not found');
    }

    // Default to current month if no date provided
    const targetDate = monthDate || new Date();
    const monthStart = new Date(
      targetDate.getFullYear(),
      targetDate.getMonth(),
      1,
    );
    const monthEnd = new Date(
      targetDate.getFullYear(),
      targetDate.getMonth() + 1,
      0,
    );

    // Fetch all activities for the month
    const activities = await this.activityRepository.find({
      where: {
        childId,
        startedAt: Between(monthStart, monthEnd),
      },
      order: { startedAt: 'ASC' },
    });

    // Calculate monthly summary
    const summary = this.calculateMonthlySummary(activities);

    // Analyze trends
    const trends = this.analyzeTrends(activities);

    // Generate weekly breakdown
    const weeklyBreakdown: WeeklyReport[] = [];
    let currentWeekStart = new Date(monthStart);

    while (currentWeekStart < monthEnd) {
      const weekReport = await this.generateWeeklyReport(
        childId,
        currentWeekStart,
      );
      weeklyBreakdown.push(weekReport);
      currentWeekStart = new Date(
        currentWeekStart.getTime() + 7 * 24 * 60 * 60 * 1000,
      );
    }

    return {
      childId,
      childName: child.name,
      month: `${targetDate.getFullYear()}-${String(targetDate.getMonth() + 1).padStart(2, '0')}`,
      summary,
      trends,
      weeklyBreakdown,
    };
  }

  /**
   * Export data in JSON, CSV, or PDF format
   */
  async exportData(
    childId: string,
    startDate: Date | null = null,
    endDate: Date | null = null,
    format: 'json' | 'csv' | 'pdf' = 'json',
  ): Promise<ExportData> {
    const start = startDate || new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
    const end = endDate || new Date();

    const child = await this.childRepository.findOne({
      where: { id: childId },
    });
    if (!child) {
      throw new Error('Child not found');
    }

    const activities = await this.activityRepository.find({
      where: {
        childId,
        startedAt: Between(start, end),
      },
      order: { startedAt: 'ASC' },
    });

    let data: any;
    let contentType: string;

    if (format === 'csv') {
      data = this.convertToCSV(activities);
      contentType = 'text/csv';
    } else if (format === 'pdf') {
      data = await this.generatePDFReport(child, activities, start, end);
      contentType = 'application/pdf';
    } else {
      data = activities;
      contentType = 'application/json';
    }

    return {
      format,
      data,
      generatedAt: new Date(),
      contentType,
    };
  }

  /**
   * Calculate weekly summary statistics
   */
  private calculateWeeklySummary(activities: Activity[]) {
    const sleepActivities = activities.filter(
      (a) => a.type === ActivityType.SLEEP && a.endedAt,
    );
    const feedingActivities = activities.filter(
      (a) => a.type === ActivityType.FEEDING,
    );
    const diaperActivities = activities.filter(
      (a) => a.type === ActivityType.DIAPER,
    );

    // Calculate total sleep in minutes
    const totalSleep = sleepActivities.reduce((total, a) => {
      const duration =
        (a.endedAt!.getTime() - a.startedAt.getTime()) / (1000 * 60);
      return total + duration;
    }, 0);

    // Identify night sleeps and naps
    const nightSleeps = sleepActivities.filter((a) => {
      const hour = a.startedAt.getHours();
      return hour >= 18 || hour <= 6;
    });
    const naps = sleepActivities.filter((a) => {
      const hour = a.startedAt.getHours();
      return hour >= 7 && hour < 18;
    });

    // Calculate averages (assuming 7 days)
    const days = 7;
    const averageSleepPerNight =
      nightSleeps.length > 0
        ? nightSleeps.reduce((total, a) => {
            const duration =
              (a.endedAt!.getTime() - a.startedAt.getTime()) / (1000 * 60);
            return total + duration;
          }, 0) / Math.max(1, nightSleeps.length / days)
        : 0;

    const averageNapsPerDay = naps.length / days;

    return {
      totalSleep: Math.round(totalSleep),
      totalFeedings: feedingActivities.length,
      totalDiaperChanges: diaperActivities.length,
      averageSleepPerNight: Math.round(averageSleepPerNight),
      averageNapsPerDay: Math.round(averageNapsPerDay * 10) / 10,
    };
  }

  /**
   * Calculate monthly summary statistics
   */
  private calculateMonthlySummary(activities: Activity[]) {
    const sleepActivities = activities.filter(
      (a) => a.type === ActivityType.SLEEP && a.endedAt,
    );
    const feedingActivities = activities.filter(
      (a) => a.type === ActivityType.FEEDING,
    );
    const diaperActivities = activities.filter(
      (a) => a.type === ActivityType.DIAPER,
    );

    const totalSleep = sleepActivities.reduce((total, a) => {
      const duration =
        (a.endedAt!.getTime() - a.startedAt.getTime()) / (1000 * 60);
      return total + duration;
    }, 0);

    // Calculate days in period
    const days =
      activities.length > 0
        ? Math.ceil(
            (activities[activities.length - 1].startedAt.getTime() -
              activities[0].startedAt.getTime()) /
              (1000 * 60 * 60 * 24),
          )
        : 1;

    const weeks = days / 7;

    return {
      totalSleep: Math.round(totalSleep),
      totalFeedings: feedingActivities.length,
      totalDiaperChanges: diaperActivities.length,
      weeklyAverages: {
        sleep: Math.round(totalSleep / weeks),
        feedings: Math.round(feedingActivities.length / weeks),
        diapers: Math.round(diaperActivities.length / weeks),
      },
    };
  }

  /**
   * Analyze trends over time
   */
  private analyzeTrends(activities: Activity[]) {
    const sleepActivities = activities.filter(
      (a) => a.type === ActivityType.SLEEP && a.endedAt,
    );
    const feedingActivities = activities.filter(
      (a) => a.type === ActivityType.FEEDING,
    );
    const milestoneActivities = activities.filter(
      (a) => a.type === ActivityType.MILESTONE,
    );

    // Analyze sleep trend
    const firstHalfSleep = sleepActivities
      .slice(0, Math.floor(sleepActivities.length / 2))
      .reduce((total, a) => {
        const duration =
          (a.endedAt!.getTime() - a.startedAt.getTime()) / (1000 * 60);
        return total + duration;
      }, 0);
    const secondHalfSleep = sleepActivities
      .slice(Math.floor(sleepActivities.length / 2))
      .reduce((total, a) => {
        const duration =
          (a.endedAt!.getTime() - a.startedAt.getTime()) / (1000 * 60);
        return total + duration;
      }, 0);

    const sleepTrend: 'improving' | 'stable' | 'declining' =
      secondHalfSleep > firstHalfSleep * 1.1
        ? 'improving'
        : secondHalfSleep < firstHalfSleep * 0.9
          ? 'declining'
          : 'stable';

    // Analyze feeding trend
    const firstHalfFeedings = Math.floor(feedingActivities.length / 2);
    const secondHalfFeedings = feedingActivities.length - firstHalfFeedings;

    const feedingTrend: 'increasing' | 'stable' | 'decreasing' =
      secondHalfFeedings > firstHalfFeedings * 1.2
        ? 'increasing'
        : secondHalfFeedings < firstHalfFeedings * 0.8
          ? 'decreasing'
          : 'stable';

    return {
      sleepTrend,
      feedingTrend,
      growthMilestones: milestoneActivities.length,
    };
  }

  /**
   * Generate highlights for the week
   */
  private generateHighlights(
    summary: any,
    patterns: PatternInsights,
  ): string[] {
    const highlights: string[] = [];

    // Sleep highlights
    if (patterns.sleep && patterns.sleep.consistency > 0.8) {
      highlights.push(
        `Excellent sleep consistency at ${Math.round(patterns.sleep.consistency * 100)}%`,
      );
    }

    // Feeding highlights
    if (patterns.feeding && patterns.feeding.consistency > 0.75) {
      highlights.push('Well-established feeding routine');
    }

    // General highlights
    if (summary.totalFeedings >= 35) {
      highlights.push(
        `Healthy feeding frequency with ${summary.totalFeedings} feedings this week`,
      );
    }

    if (summary.totalSleep >= 7000) {
      // ~100 hours
      highlights.push('Getting adequate sleep for age');
    }

    return highlights;
  }

  /**
   * Convert activities to CSV format
   */
  private convertToCSV(activities: Activity[]): string {
    const headers = [
      'ID',
      'Type',
      'Started At',
      'Ended At',
      'Duration (minutes)',
      'Notes',
      'Metadata',
    ];

    const rows = activities.map((a) => {
      const duration = a.endedAt
        ? Math.round(
            (a.endedAt.getTime() - a.startedAt.getTime()) / (1000 * 60),
          )
        : null;

      return [
        a.id,
        a.type,
        a.startedAt.toISOString(),
        a.endedAt?.toISOString() || '',
        duration || '',
        a.notes || '',
        JSON.stringify(a.metadata),
      ].join(',');
    });

    return [headers.join(','), ...rows].join('\n');
  }

  /**
   * Generate PDF report for activities
   */
  private async generatePDFReport(
    child: Child,
    activities: Activity[],
    startDate: Date,
    endDate: Date,
  ): Promise<Buffer> {
    return new Promise((resolve, reject) => {
      const doc = new PDFDocument({
        size: 'LETTER',
        margins: { top: 50, bottom: 50, left: 50, right: 50 },
      });

      const buffers: Buffer[] = [];
      doc.on('data', buffers.push.bind(buffers));
      doc.on('end', () => resolve(Buffer.concat(buffers)));
      doc.on('error', reject);

      // Header
      doc
        .fontSize(20)
        .font('Helvetica-Bold')
        .text('Activity Report', { align: 'center' });
      doc.moveDown(0.5);

      // Child info
      doc.fontSize(14).font('Helvetica');
      doc.text(`Child: ${child.name}`, { align: 'center' });
      doc.text(
        `Period: ${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()}`,
        { align: 'center' },
      );
      doc.text(`Generated: ${new Date().toLocaleString()}`, {
        align: 'center',
      });
      doc.moveDown(1);

      // Summary statistics
      const sleepActivities = activities.filter(
        (a) => a.type === ActivityType.SLEEP && a.endedAt,
      );
      const feedingActivities = activities.filter(
        (a) => a.type === ActivityType.FEEDING,
      );
      const diaperActivities = activities.filter(
        (a) => a.type === ActivityType.DIAPER,
      );

      const totalSleep = sleepActivities.reduce((total, a) => {
        const duration =
          (a.endedAt!.getTime() - a.startedAt.getTime()) / (1000 * 60);
        return total + duration;
      }, 0);

      doc
        .fontSize(16)
        .font('Helvetica-Bold')
        .text('Summary', { underline: true });
      doc.moveDown(0.5);
      doc.fontSize(12).font('Helvetica');
      doc.text(`Total Activities: ${activities.length}`);
      doc.text(
        `Sleep Sessions: ${sleepActivities.length} (${Math.round(totalSleep / 60)} hours total)`,
      );
      doc.text(`Feedings: ${feedingActivities.length}`);
      doc.text(`Diaper Changes: ${diaperActivities.length}`);
      doc.moveDown(1);

      // Activity Details by Type
      doc
        .fontSize(16)
        .font('Helvetica-Bold')
        .text('Activity Details', { underline: true });
      doc.moveDown(0.5);

      // Group activities by type
      const activityGroups = {
        [ActivityType.SLEEP]: sleepActivities,
        [ActivityType.FEEDING]: feedingActivities,
        [ActivityType.DIAPER]: diaperActivities,
      };

      Object.entries(activityGroups).forEach(([type, typeActivities]) => {
        if (typeActivities.length === 0) return;

        doc
          .fontSize(14)
          .font('Helvetica-Bold')
          .text(`${type.charAt(0).toUpperCase() + type.slice(1)} Activities`, {
            continued: false,
          });
        doc.moveDown(0.3);

        doc.fontSize(10).font('Helvetica');
        typeActivities.slice(0, 50).forEach((a, index) => {
          // Limit to 50 per type to avoid huge PDFs
          const startTime = a.startedAt.toLocaleString();
          const endTime = a.endedAt ? a.endedAt.toLocaleString() : 'Ongoing';
          const duration = a.endedAt
            ? `${Math.round((a.endedAt.getTime() - a.startedAt.getTime()) / (1000 * 60))} min`
            : '';

          doc.text(
            `  ${index + 1}. ${startTime} - ${endTime}${duration ? ` (${duration})` : ''}`,
            { continued: false },
          );

          if (a.notes) {
            doc.fontSize(9).fillColor('gray').text(`     Notes: ${a.notes}`, {
              continued: false,
            });
            doc.fillColor('black').fontSize(10);
          }
        });

        if (typeActivities.length > 50) {
          doc
            .fontSize(9)
            .fillColor('gray')
            .text(`     ... and ${typeActivities.length - 50} more`, {
              continued: false,
            });
          doc.fillColor('black').fontSize(10);
        }

        doc.moveDown(0.5);
      });

      // Footer
      doc
        .fontSize(8)
        .fillColor('gray')
        .text(
          '📱 Generated by Maternal App - For pediatrician review',
          50,
          doc.page.height - 50,
          { align: 'center' },
        );

      doc.end();
    });
  }
}
