import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";
import type { UserReport } from "@fitai/shared";

export class PDFGenerator {
  private doc: jsPDF;
  private pageWidth: number;
  private pageHeight: number;
  private margin: { top: number; right: number; bottom: number; left: number };
  private currentY: number = 0;

  // Theme colors (from globals.css)
  private colors = {
    primary: [60, 130, 225] as [number, number, number],
    primaryDark: [40, 80, 180] as [number, number, number],
    success: [46, 160, 67] as [number, number, number],
    warning: [240, 185, 11] as [number, number, number],
    destructive: [215, 50, 50] as [number, number, number],
    text: [30, 30, 30] as [number, number, number],
    textLight: [100, 100, 100] as [number, number, number],
    background: [245, 245, 245] as [number, number, number],
    white: [255, 255, 255] as [number, number, number],
  };

  constructor() {
    this.doc = new jsPDF({
      orientation: "portrait",
      unit: "mm",
      format: "a4",
    });
    this.pageWidth = this.doc.internal.pageSize.getWidth();
    this.pageHeight = this.doc.internal.pageSize.getHeight();
    this.margin = { top: 20, right: 20, bottom: 20, left: 20 };
  }

  /**
   * Generate a complete user report PDF
   */
  generateUserReport(report: UserReport): jsPDF {
    this.addHeader(report);
    this.addUserInfo(report);
    this.addReportPeriod(report);
    this.addWeeklyCheckIns(report.weeklyCheckIns);
    this.addNutritionSummary(report.nutrition);
    this.addHydrationSummary(report.hydration);
    this.addGoalsSummary(report.goals);
    this.addProfileHistory(report.profileHistory);
    this.addRecommendations(report.recommendations);
    this.addFooter();

    return this.doc;
  }

  /**
   * Save PDF to file
   */
  save(filename: string): void {
    this.doc.save(filename);
  }

  /**
   * Get PDF as blob for API response
   */
  toBlob(): Blob {
    return this.doc.output("blob");
  }

  /**
   * Get PDF as base64 for API response
   */
  toBase64(): string {
    return this.doc.output("datauristring").split(",")[1];
  }

  /**
   * Add header with title and FitAI branding
   */
  private addHeader(report: UserReport): void {
    this.currentY = this.margin.top;

    // Header background
    this.doc.setFillColor(...this.colors.primary);
    this.doc.rect(0, 0, this.pageWidth, 35, "F");

    // Title
    this.doc.setTextColor(...this.colors.white);
    this.doc.setFontSize(24);
    this.doc.setFont("helvetica", "bold");
    this.doc.text("FitAI User Report", this.margin.left, 20);

    // Subtitle
    this.doc.setFontSize(10);
    this.doc.setFont("helvetica", "normal");
    this.doc.text(
      `Generated: ${new Date().toLocaleDateString("en-US", {
        year: "numeric",
        month: "long",
        day: "numeric",
        hour: "2-digit",
        minute: "2-digit",
      })}`,
      this.margin.left,
      28,
    );

    // FitAI logo text
    this.doc.setFontSize(14);
    this.doc.setFont("helvetica", "bold");
    this.doc.text("FitAI", this.pageWidth - this.margin.right - 20, 20);

    this.currentY = 45;
  }

  /**
   * Add user information section
   */
  private addUserInfo(report: UserReport): void {
    this.checkPageBreak(30);

    this.doc.setTextColor(...this.colors.primary);
    this.doc.setFontSize(14);
    this.doc.setFont("helvetica", "bold");
    this.doc.text("User Information", this.margin.left, this.currentY);

    this.currentY += 8;

    const user = report.user;
    const client = report.client;
    const profile = report.fitnessProfile;

    const userInfo = [
      ["Name", `${user.firstName} ${user.lastName}`],
      ["Email", user.email],
      ["Phone", user.phone || "N/A"],
      ["Role", user.role],
      [
        "Member Since",
        user.createdAt ? new Date(user.createdAt).toLocaleDateString() : "N/A",
      ],
    ];

    if (client) {
      userInfo.push([
        "Membership",
        `${client.membershipType} - ${client.membershipStatus}`,
      ]);
      userInfo.push([
        "Join Date",
        new Date(client.joinDate).toLocaleDateString(),
      ]);
    }

    if (profile) {
      userInfo.push([
        "Fitness Profile",
        `Height: ${profile.height || "N/A"}cm, Weight: ${profile.weight || "N/A"}kg`,
      ]);
    }

    autoTable(this.doc, {
      startY: this.currentY,
      body: userInfo,
      theme: "plain",
      margin: { left: this.margin.left, right: this.margin.right },
      columnStyles: {
        0: { fontStyle: "bold", cellWidth: 40, fontSize: 10 },
        1: { cellWidth: "auto", fontSize: 10 },
      },
      styles: {
        cellPadding: 3,
        textColor: this.colors.text,
      },
      tableWidth: "auto" as any,
    });

    this.currentY = (this.doc as any).lastAutoTable.finalY + 15;
  }

  /**
   * Add report period section
   */
  private addReportPeriod(report: UserReport): void {
    this.checkPageBreak(25);

    this.doc.setTextColor(...this.colors.primary);
    this.doc.setFontSize(14);
    this.doc.setFont("helvetica", "bold");
    this.doc.text("Report Period", this.margin.left, this.currentY);

    this.currentY += 8;

    const periodInfo = [
      ["Start Date", report.reportPeriod.startDate],
      ["End Date", report.reportPeriod.endDate],
      [
        "Duration",
        `${Math.ceil(
          (new Date(report.reportPeriod.endDate).getTime() -
            new Date(report.reportPeriod.startDate).getTime()) /
            (1000 * 60 * 60 * 24),
        )} days`,
      ],
    ];

    autoTable(this.doc, {
      startY: this.currentY,
      body: periodInfo,
      theme: "plain",
      margin: { left: this.margin.left, right: this.margin.right },
      columnStyles: {
        0: { fontStyle: "bold", cellWidth: 40, fontSize: 10 },
        1: { cellWidth: "auto", fontSize: 10 },
      },
      styles: {
        cellPadding: 3,
        textColor: this.colors.text,
      },
      tableWidth: "auto" as any,
    });

    this.currentY = (this.doc as any).lastAutoTable.finalY + 15;
  }

  /**
   * Add weekly check-ins section
   */
  private addWeeklyCheckIns(
    weeklyCheckIns: UserReport["weeklyCheckIns"],
  ): void {
    this.checkPageBreak(50);

    this.doc.setTextColor(...this.colors.primary);
    this.doc.setFontSize(14);
    this.doc.setFont("helvetica", "bold");
    this.doc.text("Weekly Check-ins", this.margin.left, this.currentY);

    this.currentY += 8;

    if (weeklyCheckIns.length === 0) {
      this.doc.setTextColor(...this.colors.textLight);
      this.doc.setFontSize(10);
      this.doc.setFont("helvetica", "italic");
      this.doc.text(
        "No check-in data available for this period.",
        this.margin.left,
        this.currentY,
      );
      this.currentY += 15;
      return;
    }

    const tableData = weeklyCheckIns.map((week) => [
      week.weekStart,
      week.weekEnd,
      week.totalCheckIns.toString(),
      `${week.totalTimeMinutes} min`,
      `${week.averageDurationMinutes} min`,
    ]);

    autoTable(this.doc, {
      startY: this.currentY,
      head: [
        ["Week Start", "Week End", "Check-ins", "Total Time", "Avg Duration"],
      ],
      body: tableData,
      theme: "striped",
      headStyles: {
        fillColor: this.colors.primary,
        textColor: this.colors.white,
        fontStyle: "bold",
        fontSize: 9,
      },
      bodyStyles: {
        fontSize: 9,
        textColor: this.colors.text,
      },
      alternateRowStyles: {
        fillColor: [250, 250, 250],
      },
      margin: { left: this.margin.left, right: this.margin.right },
    });

    this.currentY = (this.doc as any).lastAutoTable.finalY + 15;
  }

  /**
   * Add nutrition summary section
   */
  private addNutritionSummary(nutrition: UserReport["nutrition"]): void {
    this.checkPageBreak(60);

    this.doc.setTextColor(...this.colors.primary);
    this.doc.setFontSize(14);
    this.doc.setFont("helvetica", "bold");
    this.doc.text("Nutrition Summary", this.margin.left, this.currentY);

    this.currentY += 8;

    const summaryStats = [
      ["Total Days Tracked", nutrition.totalDays.toString()],
      [
        "Average Daily Calories",
        nutrition.averageDailyCalories.toLocaleString(),
      ],
      [
        "Days Met Goal (±10%)",
        `${nutrition.daysMetGoal} / ${nutrition.totalDays}`,
      ],
    ];

    autoTable(this.doc, {
      startY: this.currentY,
      body: summaryStats,
      theme: "plain",
      margin: { left: this.margin.left, right: this.margin.right },
      columnStyles: {
        0: { fontStyle: "bold", cellWidth: 60, fontSize: 10 },
        1: { cellWidth: "auto", fontSize: 10 },
      },
      styles: {
        cellPadding: 3,
        textColor: this.colors.text,
      },
      tableWidth: "auto" as any,
    });

    this.currentY = (this.doc as any).lastAutoTable.finalY + 10;

    // Add daily breakdown if available
    if (
      nutrition.dailySummaries.length > 0 &&
      nutrition.dailySummaries.length <= 14
    ) {
      this.doc.setTextColor(...this.colors.textLight);
      this.doc.setFontSize(10);
      this.doc.setFont("helvetica", "italic");
      this.doc.text("Daily Breakdown", this.margin.left, this.currentY);

      this.currentY += 5;

      const dailyData = nutrition.dailySummaries.map((day) => [
        day.date,
        day.totalCalories.toLocaleString(),
        day.calorieGoal.toLocaleString(),
        day.caloriesDelta > 0
          ? `+${day.caloriesDelta}`
          : day.caloriesDelta.toString(),
        day.mealsCount.toString(),
      ]);

      autoTable(this.doc, {
        startY: this.currentY,
        head: [["Date", "Calories", "Goal", "Delta", "Meals"]],
        body: dailyData,
        theme: "striped",
        headStyles: {
          fillColor: this.colors.primary,
          textColor: this.colors.white,
          fontStyle: "bold",
          fontSize: 8,
        },
        bodyStyles: {
          fontSize: 8,
          textColor: this.colors.text,
        },
        alternateRowStyles: {
          fillColor: [250, 250, 250],
        },
        margin: { left: this.margin.left, right: this.margin.right },
      });

      this.currentY = (this.doc as any).lastAutoTable.finalY + 15;
    } else if (nutrition.dailySummaries.length > 14) {
      this.doc.setTextColor(...this.colors.textLight);
      this.doc.setFontSize(9);
      this.doc.text(
        `Daily breakdown (${nutrition.dailySummaries.length} days) - See dashboard for detailed view.`,
        this.margin.left,
        this.currentY,
      );
      this.currentY += 15;
    }
  }

  /**
   * Add hydration summary section
   */
  private addHydrationSummary(hydration: UserReport["hydration"]): void {
    this.checkPageBreak(50);

    this.doc.setTextColor(...this.colors.primary);
    this.doc.setFontSize(14);
    this.doc.setFont("helvetica", "bold");
    this.doc.text("Hydration Summary", this.margin.left, this.currentY);

    this.currentY += 8;

    const summaryStats = [
      ["Total Days Tracked", hydration.totalDays.toString()],
      ["Average Daily Water", `${hydration.averageDailyWater} ml`],
      ["Days Met Goal", `${hydration.daysMetGoal} / ${hydration.totalDays}`],
    ];

    autoTable(this.doc, {
      startY: this.currentY,
      body: summaryStats,
      theme: "plain",
      margin: { left: this.margin.left, right: this.margin.right },
      columnStyles: {
        0: { fontStyle: "bold", cellWidth: 60, fontSize: 10 },
        1: { cellWidth: "auto", fontSize: 10 },
      },
      styles: {
        cellPadding: 3,
        textColor: this.colors.text,
      },
      tableWidth: "auto" as any,
    });

    this.currentY = (this.doc as any).lastAutoTable.finalY + 10;

    // Add daily breakdown if available
    if (
      hydration.dailySummaries.length > 0 &&
      hydration.dailySummaries.length <= 14
    ) {
      this.doc.setTextColor(...this.colors.textLight);
      this.doc.setFontSize(10);
      this.doc.setFont("helvetica", "italic");
      this.doc.text("Daily Breakdown", this.margin.left, this.currentY);

      this.currentY += 5;

      const dailyData = hydration.dailySummaries.map((day) => [
        day.date,
        `${day.totalWater} ml`,
        `${day.waterGoal} ml`,
        `${day.hydrationPercentage}%`,
      ]);

      autoTable(this.doc, {
        startY: this.currentY,
        head: [["Date", "Total Water", "Goal", "Achievement"]],
        body: dailyData,
        theme: "striped",
        headStyles: {
          fillColor: this.colors.primary,
          textColor: this.colors.white,
          fontStyle: "bold",
          fontSize: 8,
        },
        bodyStyles: {
          fontSize: 8,
          textColor: this.colors.text,
        },
        alternateRowStyles: {
          fillColor: [250, 250, 250],
        },
        margin: { left: this.margin.left, right: this.margin.right },
      });

      this.currentY = (this.doc as any).lastAutoTable.finalY + 15;
    } else if (hydration.dailySummaries.length > 14) {
      this.doc.setTextColor(...this.colors.textLight);
      this.doc.setFontSize(9);
      this.doc.text(
        `Daily breakdown (${hydration.dailySummaries.length} days) - See dashboard for detailed view.`,
        this.margin.left,
        this.currentY,
      );
      this.currentY += 15;
    }
  }

  /**
   * Add goals summary section
   */
  private addGoalsSummary(goals: UserReport["goals"]): void {
    this.checkPageBreak(60);

    this.doc.setTextColor(...this.colors.primary);
    this.doc.setFontSize(14);
    this.doc.setFont("helvetica", "bold");
    this.doc.text("Fitness Goals", this.margin.left, this.currentY);

    this.currentY += 8;

    const summaryStats = [
      ["Active Goals", goals.totalActive.toString()],
      ["Completed Goals", goals.totalCompleted.toString()],
      ["Average Progress", `${goals.averageProgress}%`],
    ];

    autoTable(this.doc, {
      startY: this.currentY,
      body: summaryStats,
      theme: "plain",
      margin: { left: this.margin.left, right: this.margin.right },
      columnStyles: {
        0: { fontStyle: "bold", cellWidth: 60, fontSize: 10 },
        1: { cellWidth: "auto", fontSize: 10 },
      },
      styles: {
        cellPadding: 3,
        textColor: this.colors.text,
      },
      tableWidth: "auto" as any,
    });

    this.currentY = (this.doc as any).lastAutoTable.finalY + 10;

    // Add active goals
    if (goals.active.length > 0) {
      this.doc.setTextColor(...this.colors.success);
      this.doc.setFontSize(10);
      this.doc.setFont("helvetica", "bold");
      this.doc.text("Active Goals", this.margin.left, this.currentY);

      this.currentY += 5;

      const activeGoals = goals.active.map((goal) => [
        goal.title,
        goal.goalType,
        `${goal.progress}%`,
        goal.priority,
      ]);

      autoTable(this.doc, {
        startY: this.currentY,
        head: [["Title", "Type", "Progress", "Priority"]],
        body: activeGoals,
        theme: "striped",
        headStyles: {
          fillColor: this.colors.success,
          textColor: this.colors.white,
          fontStyle: "bold",
          fontSize: 8,
        },
        bodyStyles: {
          fontSize: 8,
          textColor: this.colors.text,
        },
        alternateRowStyles: {
          fillColor: [250, 250, 250],
        },
        margin: { left: this.margin.left, right: this.margin.right },
      });

      this.currentY = (this.doc as any).lastAutoTable.finalY + 10;
    }

    // Add completed goals
    if (goals.completed.length > 0) {
      this.doc.setTextColor(...this.colors.primary);
      this.doc.setFontSize(10);
      this.doc.setFont("helvetica", "bold");
      this.doc.text("Completed Goals", this.margin.left, this.currentY);

      this.currentY += 5;

      const completedGoals = goals.completed.map((goal) => [
        goal.title,
        goal.goalType,
        goal.completedDate
          ? new Date(goal.completedDate).toLocaleDateString()
          : "N/A",
      ]);

      autoTable(this.doc, {
        startY: this.currentY,
        head: [["Title", "Type", "Completed Date"]],
        body: completedGoals,
        theme: "striped",
        headStyles: {
          fillColor: this.colors.primary,
          textColor: this.colors.white,
          fontStyle: "bold",
          fontSize: 8,
        },
        bodyStyles: {
          fontSize: 8,
          textColor: this.colors.text,
        },
        alternateRowStyles: {
          fillColor: [250, 250, 250],
        },
        margin: { left: this.margin.left, right: this.margin.right },
      });

      this.currentY = (this.doc as any).lastAutoTable.finalY + 15;
    }
  }

  /**
   * Add profile history section
   */
  private addProfileHistory(history: UserReport["profileHistory"]): void {
    this.checkPageBreak(50);

    this.doc.setTextColor(...this.colors.primary);
    this.doc.setFontSize(14);
    this.doc.setFont("helvetica", "bold");
    this.doc.text("Fitness Profile Changes", this.margin.left, this.currentY);

    this.currentY += 8;

    if (history.length === 0) {
      this.doc.setTextColor(...this.colors.textLight);
      this.doc.setFontSize(10);
      this.doc.setFont("helvetica", "italic");
      this.doc.text(
        "No profile changes recorded during this period.",
        this.margin.left,
        this.currentY,
      );
      this.currentY += 15;
      return;
    }

    const historyData = history.map((item) => [
      item.fieldName,
      item.changeType,
      item.previousValue || "N/A",
      item.newValue || "N/A",
      new Date(item.changedAt).toLocaleDateString(),
    ]);

    autoTable(this.doc, {
      startY: this.currentY,
      head: [["Field", "Change Type", "Previous", "New", "Date"]],
      body: historyData,
      theme: "striped",
      headStyles: {
        fillColor: this.colors.primary,
        textColor: this.colors.white,
        fontStyle: "bold",
        fontSize: 8,
      },
      bodyStyles: {
        fontSize: 8,
        textColor: this.colors.text,
      },
      alternateRowStyles: {
        fillColor: [250, 250, 250],
      },
      margin: { left: this.margin.left, right: this.margin.right },
    });

    this.currentY = (this.doc as any).lastAutoTable.finalY + 15;
  }

  /**
   * Add recommendations section
   */
  private addRecommendations(
    recommendations: UserReport["recommendations"],
  ): void {
    this.checkPageBreak(60);

    this.doc.setTextColor(...this.colors.primary);
    this.doc.setFontSize(14);
    this.doc.setFont("helvetica", "bold");
    this.doc.text("AI Recommendations", this.margin.left, this.currentY);

    this.currentY += 8;

    const summaryStats = [
      ["Accepted", recommendations.totalAccepted.toString()],
      ["Rejected", recommendations.totalRejected.toString()],
      ["Pending", recommendations.totalPending.toString()],
    ];

    autoTable(this.doc, {
      startY: this.currentY,
      body: summaryStats,
      theme: "plain",
      margin: { left: this.margin.left, right: this.margin.right },
      columnStyles: {
        0: { fontStyle: "bold", cellWidth: 60, fontSize: 10 },
        1: { cellWidth: "auto", fontSize: 10 },
      },
      styles: {
        cellPadding: 3,
        textColor: this.colors.text,
      },
      tableWidth: "auto" as any,
    });

    this.currentY = (this.doc as any).lastAutoTable.finalY + 10;

    // Add accepted recommendations
    if (recommendations.accepted.length > 0) {
      this.doc.setTextColor(...this.colors.success);
      this.doc.setFontSize(10);
      this.doc.setFont("helvetica", "bold");
      this.doc.text(
        "Accepted Recommendations",
        this.margin.left,
        this.currentY,
      );

      this.currentY += 5;

      const acceptedRecs = recommendations.accepted.map((rec) => [
        rec.recommendationText.substring(0, 80) +
          (rec.recommendationText.length > 80 ? "..." : ""),
        new Date(rec.generatedAt).toLocaleDateString(),
      ]);

      autoTable(this.doc, {
        startY: this.currentY,
        head: [["Recommendation", "Generated"]],
        body: acceptedRecs,
        theme: "striped",
        headStyles: {
          fillColor: this.colors.success,
          textColor: this.colors.white,
          fontStyle: "bold",
          fontSize: 8,
        },
        bodyStyles: {
          fontSize: 8,
          textColor: this.colors.text,
        },
        alternateRowStyles: {
          fillColor: [250, 250, 250],
        },
        margin: { left: this.margin.left, right: this.margin.right },
      });

      this.currentY = (this.doc as any).lastAutoTable.finalY + 10;
    }

    // Add pending recommendations
    if (recommendations.pending.length > 0) {
      this.doc.setTextColor(...this.colors.warning);
      this.doc.setFontSize(10);
      this.doc.setFont("helvetica", "bold");
      this.doc.text("Pending Recommendations", this.margin.left, this.currentY);

      this.currentY += 5;

      const pendingRecs = recommendations.pending.map((rec) => [
        rec.recommendationText.substring(0, 80) +
          (rec.recommendationText.length > 80 ? "..." : ""),
        new Date(rec.generatedAt).toLocaleDateString(),
      ]);

      autoTable(this.doc, {
        startY: this.currentY,
        head: [["Recommendation", "Generated"]],
        body: pendingRecs,
        theme: "striped",
        headStyles: {
          fillColor: this.colors.warning,
          textColor: this.colors.text,
          fontStyle: "bold",
          fontSize: 8,
        },
        bodyStyles: {
          fontSize: 8,
          textColor: this.colors.text,
        },
        alternateRowStyles: {
          fillColor: [250, 250, 250],
        },
        margin: { left: this.margin.left, right: this.margin.right },
      });

      this.currentY = (this.doc as any).lastAutoTable.finalY + 15;
    }
  }

  /**
   * Add footer to each page
   */
  private addFooter(): void {
    const pageCount = this.doc.getNumberOfPages();

    for (let i = 1; i <= pageCount; i++) {
      this.doc.setPage(i);

      // Footer line
      this.doc.setDrawColor(...this.colors.primary);
      this.doc.setLineWidth(0.5);
      this.doc.line(
        this.margin.left,
        this.pageHeight - 15,
        this.pageWidth - this.margin.right,
        this.pageHeight - 15,
      );

      // Footer text
      this.doc.setTextColor(...this.colors.textLight);
      this.doc.setFontSize(8);
      this.doc.setFont("helvetica", "normal");
      this.doc.text(
        `FitAI User Report - Page ${i} of ${pageCount}`,
        this.margin.left,
        this.pageHeight - 10,
      );
      this.doc.text(
        "Generated by FitAI",
        this.pageWidth - this.margin.right - 30,
        this.pageHeight - 10,
      );
    }
  }

  /**
   * Check if we need a page break
   */
  private checkPageBreak(requiredSpace: number): void {
    if (this.currentY + requiredSpace > this.pageHeight - this.margin.bottom) {
      this.doc.addPage();
      this.currentY = this.margin.top;
    }
  }
}

export default PDFGenerator;
