import { parseLinkedInCSV } from "./parsers/csv-parser.js";
import {
  getAnalyticsRoot,
  ensureDirectories,
  saveBatch,
  loadAllPosts,
} from "./utils/storage.js";
import { detectAlerts } from "./utils/alerts.js";
import { mean, standardDeviation } from "./utils/stats.js";
import { generateWeeklyReport, getCurrentISOWeek } from "./reports/weekly.js";
import { generateHeatmap } from "./reports/heatmap.js";
import { generateMonthlyReport } from "./reports/monthly.js";
import { join } from "node:path";
import { existsSync } from "node:fs";
import type { PostMetrics } from "./models/types.js";

const args = process.argv.slice(2);
const command = args[0];

function parseOption(args: string[], flag: string): string | undefined {
  const idx = args.indexOf(flag);
  return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
}

function printUsage() {
  console.log(`
LinkedIn Analytics CLI

Usage:
  node build/cli.js import <filename>           Import a CSV export
  node build/cli.js report [--week W]            Generate weekly report
  node build/cli.js report --month YYYY-MM       Generate monthly report with MoM comparison
  node build/cli.js trends [--period P] [--metric M]  Show trends and alerts
  node build/cli.js heatmap                           Day-of-week performance matrix

Options:
  --week W       ISO week (e.g., 2026-W05), defaults to current week
  --period P     Time period: "week" | "month" | "quarter" | "all" (default: "month")
  --metric M     Metric to analyze: "impressions" | "reactions" | "comments" | "shares" | "clicks" | "engagementRate" (default: "impressions")

Examples:
  node build/cli.js import linkedin-export-2026-01-20.csv
  node build/cli.js report --week 2026-W04
  node build/cli.js trends --period quarter --metric engagementRate
  `);
}

async function handleImport(root: string, args: string[]) {
  const filename = args[1];

  if (!filename) {
    console.error("Error: Missing filename argument");
    console.error("Usage: node build/cli.js import <filename>");
    process.exit(1);
  }

  const fullPath = join(root, "exports", filename);

  if (!existsSync(fullPath)) {
    console.error(`Error: File not found: ${fullPath}`);
    console.error(`\nMake sure the CSV file is placed in: ${join(root, "exports")}`);
    process.exit(1);
  }

  console.log(`Importing ${filename}...`);

  try {
    const batch = parseLinkedInCSV(fullPath, filename);
    const savedFilename = saveBatch(root, batch);

    console.log("\nImport successful!");
    console.log("─────────────────────────────────────");
    console.log(`Posts imported:  ${batch.postCount}`);
    console.log(`Date range:      ${batch.dateRange.from} to ${batch.dateRange.to}`);
    console.log(`Batch ID:        ${batch.batchId}`);
    console.log(`Saved to:        posts/${savedFilename}`);

    // Run alert detection on imported posts
    const alerts = detectAlerts(batch.posts, "impressions");

    if (alerts.length > 0) {
      console.log("\nImmediate alerts detected:");
      console.log("─────────────────────────────────────");
      for (const alert of alerts.slice(0, 5)) {
        const icon = alert.severity === "critical" ? "🔴" : alert.severity === "warning" ? "⚠️" : "ℹ️";
        console.log(`${icon} [${alert.severity.toUpperCase()}] ${alert.message}`);
      }

      if (alerts.length > 5) {
        console.log(`\n... and ${alerts.length - 5} more alerts`);
      }
    } else {
      console.log("\nNo anomalies detected in imported data.");
    }
  } catch (err) {
    console.error(`Error parsing CSV: ${err instanceof Error ? err.message : String(err)}`);
    process.exit(1);
  }
}

async function handleReport(root: string, args: string[]) {
  const monthOption = parseOption(args, "--month");
  if (monthOption) {
    return handleMonthlyReport(root, monthOption);
  }

  const weekOption = parseOption(args, "--week");
  const week = weekOption || getCurrentISOWeek();

  console.log(`Generating weekly report for ${week}...`);

  try {
    const report = generateWeeklyReport(root, week);

    console.log("\nWeekly Report");
    console.log("═════════════════════════════════════");
    console.log(`Week:            ${report.week}`);
    console.log(`Generated at:    ${new Date(report.generatedAt).toLocaleString()}`);
    console.log();

    console.log("Summary");
    console.log("─────────────────────────────────────");
    console.log(`Total posts:         ${report.summary.totalPosts}`);
    console.log(`Total impressions:   ${report.summary.totalImpressions.toLocaleString()}`);
    console.log(`Total reactions:     ${report.summary.totalReactions.toLocaleString()}`);
    console.log(`Total comments:      ${report.summary.totalComments.toLocaleString()}`);
    console.log(`Total shares:        ${report.summary.totalShares.toLocaleString()}`);
    console.log(`Total clicks:        ${report.summary.totalClicks.toLocaleString()}`);
    console.log(`Avg engagement:      ${report.summary.avgEngagementRate.toFixed(2)}%`);
    console.log(`Avg impressions:     ${Math.round(report.summary.avgImpressionsPerPost).toLocaleString()} per post`);
    console.log();

    if (report.topPerformers.length > 0) {
      console.log("Top Performers");
      console.log("─────────────────────────────────────");
      for (const post of report.topPerformers.slice(0, 5)) {
        const title = post.title.length > 50 ? post.title.substring(0, 47) + "..." : post.title;
        console.log(`• ${title}`);
        console.log(`  ${post.metrics.impressions.toLocaleString()} impressions | ${post.metrics.engagementRate.toFixed(2)}% engagement | ${post.publishedDate}`);
      }
      console.log();
    }

    if (report.underperformers.length > 0) {
      console.log("Underperformers");
      console.log("─────────────────────────────────────");
      for (const post of report.underperformers.slice(0, 3)) {
        const title = post.title.length > 50 ? post.title.substring(0, 47) + "..." : post.title;
        console.log(`• ${title}`);
        console.log(`  ${post.metrics.impressions.toLocaleString()} impressions | ${post.metrics.engagementRate.toFixed(2)}% engagement | ${post.publishedDate}`);
      }
      console.log();
    }

    console.log("Trends");
    console.log("─────────────────────────────────────");
    console.log(`Impressions trend:   ${report.trends.impressionsTrend.toUpperCase()} (${report.trends.percentChange.impressions > 0 ? "+" : ""}${report.trends.percentChange.impressions.toFixed(1)}%)`);
    console.log(`Engagement trend:    ${report.trends.engagementTrend.toUpperCase()} (${report.trends.percentChange.engagement > 0 ? "+" : ""}${report.trends.percentChange.engagement.toFixed(1)}%)`);
    console.log(`Compared to:         ${report.trends.comparedTo}`);
    console.log();

    if (report.alerts.length > 0) {
      console.log("Alerts");
      console.log("─────────────────────────────────────");
      for (const alert of report.alerts) {
        const icon = alert.severity === "critical" ? "🔴" : alert.severity === "warning" ? "⚠️" : "ℹ️";
        console.log(`${icon} [${alert.severity.toUpperCase()}] ${alert.message}`);
      }
      console.log();
    }

    console.log(`Report saved to: weekly-reports/${week}.json`);
  } catch (err) {
    console.error(`Error generating report: ${err instanceof Error ? err.message : String(err)}`);
    process.exit(1);
  }
}

/**
 * Type guard to check if a string is a valid PostMetrics key
 */
function isPostMetric(value: string): value is keyof PostMetrics {
  const validMetrics: (keyof PostMetrics)[] = [
    "impressions",
    "reactions",
    "comments",
    "shares",
    "clicks",
    "engagementRate",
  ];
  return validMetrics.includes(value as keyof PostMetrics);
}

async function handleTrends(root: string, args: string[]) {
  const periodOption = parseOption(args, "--period") || "month";
  const metricOption = parseOption(args, "--metric") || "impressions";

  const validPeriods = ["week", "month", "quarter", "all"];

  if (!validPeriods.includes(periodOption)) {
    console.error(`Error: Invalid period "${periodOption}". Must be one of: ${validPeriods.join(", ")}`);
    process.exit(1);
  }

  if (!isPostMetric(metricOption)) {
    const validMetrics: (keyof PostMetrics)[] = [
      "impressions",
      "reactions",
      "comments",
      "shares",
      "clicks",
      "engagementRate",
    ];
    console.error(`Error: Invalid metric "${metricOption}". Must be one of: ${validMetrics.join(", ")}`);
    process.exit(1);
  }

  const period = periodOption as "week" | "month" | "quarter" | "all";
  const metric = metricOption;

  console.log(`Analyzing trends for ${metric} over ${period}...`);

  try {
    const allPosts = loadAllPosts(root);

    if (allPosts.length === 0) {
      console.error("Error: No posts found. Import some data first.");
      process.exit(1);
    }

    // Calculate date range based on period
    const now = new Date();
    let fromDate = new Date(0); // Beginning of time for "all"

    if (period === "week") {
      fromDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
    } else if (period === "month") {
      fromDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
    } else if (period === "quarter") {
      fromDate = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
    }

    const fromDateStr = fromDate.toISOString().split("T")[0];

    // Filter posts by period
    const filteredPosts = allPosts.filter(
      (post) => post.publishedDate >= fromDateStr
    );

    if (filteredPosts.length === 0) {
      console.error(`Error: No posts found in the ${period} period.`);
      process.exit(1);
    }

    // Calculate statistics
    const values = filteredPosts.map((post) => post.metrics[metric]);
    const avg = mean(values);
    const stdDev = standardDeviation(values);
    const min = Math.min(...values);
    const max = Math.max(...values);

    console.log("\nTrend Analysis");
    console.log("═════════════════════════════════════");
    console.log(`Metric:          ${metric}`);
    console.log(`Period:          ${period}`);
    console.log(`Posts analyzed:  ${filteredPosts.length}`);
    console.log(`Date range:      ${filteredPosts[filteredPosts.length - 1].publishedDate} to ${filteredPosts[0].publishedDate}`);
    console.log();

    console.log("Statistics");
    console.log("─────────────────────────────────────");
    console.log(`Mean:            ${avg.toFixed(2)}`);
    console.log(`Std deviation:   ${stdDev.toFixed(2)}`);
    console.log(`Min:             ${min.toFixed(2)}`);
    console.log(`Max:             ${max.toFixed(2)}`);
    console.log();

    // Generate alerts
    const alerts = detectAlerts(filteredPosts, metric);

    if (alerts.length > 0) {
      console.log("Alerts");
      console.log("─────────────────────────────────────");
      for (const alert of alerts) {
        const icon = alert.severity === "critical" ? "🔴" : alert.severity === "warning" ? "⚠️" : "ℹ️";
        console.log(`${icon} [${alert.severity.toUpperCase()}] ${alert.message}`);
      }
    } else {
      console.log("No anomalies detected in this period.");
    }
  } catch (err) {
    console.error(`Error analyzing trends: ${err instanceof Error ? err.message : String(err)}`);
    process.exit(1);
  }
}

async function handleMonthlyReport(root: string, month: string) {
  console.log(`Generating monthly report for ${month}...`);

  try {
    const report = generateMonthlyReport(root, month);

    console.log("\nMonthly Report");
    console.log("═════════════════════════════════════");
    console.log(`Month:           ${report.month}`);
    console.log(`Generated at:    ${new Date(report.generatedAt).toLocaleString()}`);
    console.log();

    console.log("Summary");
    console.log("─────────────────────────────────────");
    const s = report.summary;
    const fmtDelta = (val: number | null, suffix = "%") =>
      val !== null ? ` (${val > 0 ? "+" : ""}${val}${suffix})` : "";

    console.log(`Posts:               ${s.totalPosts}${fmtDelta(report.trends.percentChange.postCount)}`);
    console.log(`Impressions:         ${s.totalImpressions.toLocaleString()}${fmtDelta(report.trends.percentChange.impressions)}`);
    console.log(`Avg per post:        ${s.avgImpressionsPerPost.toLocaleString()}`);
    console.log(`Avg engagement:      ${s.avgEngagementRate.toFixed(2)}%${fmtDelta(report.trends.percentChange.engagement)}`);
    console.log(`Reactions:           ${s.totalReactions.toLocaleString()}`);
    console.log(`Comments:            ${s.totalComments.toLocaleString()}`);
    console.log(`Shares:              ${s.totalShares.toLocaleString()}`);
    console.log(`Clicks:              ${s.totalClicks.toLocaleString()}`);
    console.log();

    if (report.byWeek.length > 0) {
      console.log("Week Breakdown");
      console.log("─────────────────────────────────────");
      for (const w of report.byWeek) {
        console.log(`${w.week}: ${w.postCount} posts | ${w.avgImpressions.toLocaleString()} avg impr | ${w.avgEngagementRate.toFixed(1)}% eng`);
      }
      console.log();
    }

    if (report.topPerformers.length > 0) {
      console.log("Top Performers");
      console.log("─────────────────────────────────────");
      for (const post of report.topPerformers.slice(0, 5)) {
        const title = post.title.length > 50 ? post.title.substring(0, 47) + "..." : post.title;
        console.log(`• ${title}`);
        console.log(`  ${post.metrics.impressions.toLocaleString()} impressions | ${post.metrics.engagementRate.toFixed(2)}% eng | ${post.publishedDate}`);
      }
      console.log();
    }

    if (report.trends.comparedTo) {
      console.log(`Compared to:         ${report.trends.comparedTo}`);
    } else {
      console.log("No previous month data for comparison.");
    }
    console.log();

    if (report.alerts.length > 0) {
      console.log("Alerts");
      console.log("─────────────────────────────────────");
      for (const alert of report.alerts) {
        const icon = alert.severity === "critical" ? "🔴" : alert.severity === "warning" ? "⚠️" : "ℹ️";
        console.log(`${icon} [${alert.severity.toUpperCase()}] ${alert.message}`);
      }
      console.log();
    }

    console.log(`Report saved to: monthly-reports/${month}.json`);
  } catch (err) {
    console.error(`Error generating monthly report: ${err instanceof Error ? err.message : String(err)}`);
    process.exit(1);
  }
}

async function handleHeatmap(root: string) {
  console.log("Generating day-of-week heatmap...");

  try {
    const allPosts = loadAllPosts(root);

    if (allPosts.length === 0) {
      console.error("Error: No posts found. Import some data first.");
      process.exit(1);
    }

    const report = generateHeatmap(allPosts);

    console.log("\nDay-of-Week Performance Heatmap");
    console.log("═════════════════════════════════════");
    console.log(`Posts analyzed:  ${report.postsAnalyzed}`);
    console.log(`Date range:      ${report.dateRange.from} to ${report.dateRange.to}`);
    console.log();

    // Print table header
    const days = report.byDayOfWeek.map(d => d.dayName.slice(0, 3).padStart(7));
    console.log(`         ${days.join("")}`);
    console.log(`         ${"───────".repeat(7)}`);

    // Posts row
    const postCounts = report.byDayOfWeek.map(d => String(d.postCount).padStart(7));
    console.log(`Posts:   ${postCounts.join("")}`);

    // Impressions row
    const impressions = report.byDayOfWeek.map(d =>
      d.postCount > 0 ? d.avgImpressions.toLocaleString().padStart(7) : "      -"
    );
    console.log(`Impr:    ${impressions.join("")}`);

    // Engagement rate row
    const engRates = report.byDayOfWeek.map(d =>
      d.postCount > 0 ? `${d.avgEngagementRate.toFixed(1)}%`.padStart(7) : "      -"
    );
    console.log(`Eng:     ${engRates.join("")}`);

    console.log();
    console.log(`Best day for impressions:  ${report.bestDayImpressions}`);
    console.log(`Best day for engagement:   ${report.bestDayEngagement}`);

    console.log("\nNote: LinkedIn CSV exports do not include publish time.");
    console.log("This heatmap shows day-of-week only.");
  } catch (err) {
    console.error(`Error generating heatmap: ${err instanceof Error ? err.message : String(err)}`);
    process.exit(1);
  }
}

async function main() {
  const root = getAnalyticsRoot();
  ensureDirectories(root);

  switch (command) {
    case "import":
      await handleImport(root, args);
      break;
    case "report":
      await handleReport(root, args);
      break;
    case "trends":
      await handleTrends(root, args);
      break;
    case "heatmap":
      await handleHeatmap(root);
      break;
    default:
      printUsage();
      process.exit(command ? 1 : 0);
  }
}

main().catch((err) => {
  console.error("Fatal error:", err instanceof Error ? err.message : String(err));
  process.exit(1);
});
