import { describe, test, afterEach } from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, rmSync, writeFileSync, existsSync, readdirSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import {
  ensureDirectories,
  saveBatch,
  loadAllBatches,
  loadAllPosts,
  listExports,
  saveWeeklyReport,
  loadWeeklyReport,
  loadAllWeeklyReports,
} from "../src/utils/storage.js";
import type { AnalyticsBatch, PostAnalytics, WeeklyReport } from "../src/models/types.js";

// Helper function to create test post data
function createTestPost(overrides?: Partial<PostAnalytics>): PostAnalytics {
  return {
    id: overrides?.id || "test-post-1",
    title: overrides?.title || "Test post content",
    publishedDate: overrides?.publishedDate || "2026-01-15",
    metrics: {
      impressions: 1000,
      reactions: 50,
      comments: 10,
      shares: 5,
      clicks: 20,
      engagementRate: 8.5,
      ...(overrides?.metrics || {}),
    },
    importedAt: overrides?.importedAt || "2026-01-20T10:00:00Z",
    exportSource: overrides?.exportSource || "test-export.csv",
  };
}

// Helper function to create test batch data
function createTestBatch(overrides?: Partial<AnalyticsBatch>): AnalyticsBatch {
  const posts = overrides?.posts || [createTestPost()];
  return {
    batchId: overrides?.batchId || "12345678-1234-1234-1234-123456789abc",
    importedAt: overrides?.importedAt || "2026-01-20T10:00:00Z",
    exportFilename: overrides?.exportFilename || "test-export.csv",
    dateRange: overrides?.dateRange || { from: "2026-01-15", to: "2026-01-20" },
    postCount: posts.length,
    posts,
  };
}

// Helper function to create test weekly report
function createTestWeeklyReport(overrides?: Partial<WeeklyReport>): WeeklyReport {
  return {
    week: overrides?.week || "2026-W03",
    generatedAt: overrides?.generatedAt || "2026-01-20T10:00:00Z",
    summary: {
      totalPosts: 5,
      totalImpressions: 5000,
      totalReactions: 250,
      totalComments: 50,
      totalShares: 25,
      totalClicks: 100,
      avgEngagementRate: 8.5,
      avgImpressionsPerPost: 1000,
      ...(overrides?.summary || {}),
    },
    topPerformers: overrides?.topPerformers || [],
    underperformers: overrides?.underperformers || [],
    trends: {
      impressionsTrend: "up",
      engagementTrend: "stable",
      comparedTo: "2026-W02",
      percentChange: {
        impressions: 10,
        engagement: 2,
      },
      ...(overrides?.trends || {}),
    },
    alerts: overrides?.alerts || [],
  };
}

describe("storage", () => {
  let tempDir: string;

  // Create temp directory before each test
  function setupTempDir(): string {
    return mkdtempSync(join(tmpdir(), "analytics-test-"));
  }

  // Clean up temp directory after each test
  afterEach(() => {
    if (tempDir && existsSync(tempDir)) {
      rmSync(tempDir, { recursive: true, force: true });
    }
  });

  describe("ensureDirectories", () => {
    test("should create directories", () => {
      tempDir = setupTempDir();
      ensureDirectories(tempDir);

      assert.ok(existsSync(join(tempDir, "exports")));
      assert.ok(existsSync(join(tempDir, "posts")));
      assert.ok(existsSync(join(tempDir, "weekly-reports")));
    });

    test("should not fail if directories already exist", () => {
      tempDir = setupTempDir();
      ensureDirectories(tempDir);
      // Call again - should not throw
      ensureDirectories(tempDir);

      assert.ok(existsSync(join(tempDir, "exports")));
      assert.ok(existsSync(join(tempDir, "posts")));
      assert.ok(existsSync(join(tempDir, "weekly-reports")));
    });
  });

  describe("saveBatch", () => {
    test("should write JSON file", () => {
      tempDir = setupTempDir();
      const batch = createTestBatch();

      const filename = saveBatch(tempDir, batch);

      assert.ok(filename.startsWith("2026-01-15-"));
      assert.ok(filename.endsWith(".json"));

      const filepath = join(tempDir, "posts", filename);
      assert.ok(existsSync(filepath));

      // Verify content
      const loadedBatches = loadAllBatches(tempDir);
      assert.equal(loadedBatches.length, 1);
      assert.equal(loadedBatches[0].batchId, batch.batchId);
    });

    test("should create directories if they don't exist", () => {
      tempDir = setupTempDir();
      const batch = createTestBatch();

      saveBatch(tempDir, batch);

      assert.ok(existsSync(join(tempDir, "posts")));
    });

    test("should use short batch ID in filename", () => {
      tempDir = setupTempDir();
      const batch = createTestBatch({
        batchId: "abcdef12-3456-7890-abcd-ef1234567890",
        dateRange: { from: "2026-01-15", to: "2026-01-20" },
      });

      const filename = saveBatch(tempDir, batch);

      assert.ok(filename.startsWith("2026-01-15-abcdef12"));
    });
  });

  describe("loadAllBatches", () => {
    test("should load saved batches", () => {
      tempDir = setupTempDir();

      const batch1 = createTestBatch({
        batchId: "batch1",
        importedAt: "2026-01-20T10:00:00Z",
      });
      const batch2 = createTestBatch({
        batchId: "batch2",
        importedAt: "2026-01-21T10:00:00Z",
        dateRange: { from: "2026-01-21", to: "2026-01-21" },
      });

      saveBatch(tempDir, batch1);
      saveBatch(tempDir, batch2);

      const batches = loadAllBatches(tempDir);

      assert.equal(batches.length, 2);
      assert.equal(batches[0].batchId, "batch1");
      assert.equal(batches[1].batchId, "batch2");
    });

    test("should return empty array if posts directory doesn't exist", () => {
      tempDir = setupTempDir();

      const batches = loadAllBatches(tempDir);

      assert.deepEqual(batches, []);
    });

    test("should sort by importedAt timestamp", () => {
      tempDir = setupTempDir();

      const batch1 = createTestBatch({
        batchId: "batch1",
        importedAt: "2026-01-22T10:00:00Z",
        dateRange: { from: "2026-01-22", to: "2026-01-22" },
      });
      const batch2 = createTestBatch({
        batchId: "batch2",
        importedAt: "2026-01-20T10:00:00Z",
      });
      const batch3 = createTestBatch({
        batchId: "batch3",
        importedAt: "2026-01-21T10:00:00Z",
        dateRange: { from: "2026-01-21", to: "2026-01-21" },
      });

      saveBatch(tempDir, batch1);
      saveBatch(tempDir, batch2);
      saveBatch(tempDir, batch3);

      const batches = loadAllBatches(tempDir);

      assert.equal(batches.length, 3);
      assert.equal(batches[0].batchId, "batch2"); // Earliest
      assert.equal(batches[1].batchId, "batch3");
      assert.equal(batches[2].batchId, "batch1"); // Latest
    });
  });

  describe("loadAllPosts", () => {
    test("should deduplicate by post ID", () => {
      tempDir = setupTempDir();

      const post1 = createTestPost({
        id: "post1",
        title: "Old version",
        publishedDate: "2026-01-15",
      });
      const post1Updated = createTestPost({
        id: "post1",
        title: "New version",
        publishedDate: "2026-01-15",
      });
      const post2 = createTestPost({
        id: "post2",
        publishedDate: "2026-01-16",
      });

      const batch1 = createTestBatch({
        batchId: "batch1",
        importedAt: "2026-01-20T10:00:00Z",
        posts: [post1, post2],
      });
      const batch2 = createTestBatch({
        batchId: "batch2",
        importedAt: "2026-01-21T10:00:00Z",
        dateRange: { from: "2026-01-21", to: "2026-01-21" },
        posts: [post1Updated], // Later import of post1
      });

      saveBatch(tempDir, batch1);
      saveBatch(tempDir, batch2);

      const posts = loadAllPosts(tempDir);

      assert.equal(posts.length, 2);
      // Should have the updated version of post1
      const foundPost1 = posts.find(p => p.id === "post1");
      assert.equal(foundPost1?.title, "New version");
    });

    test("should sort by publishedDate descending", () => {
      tempDir = setupTempDir();

      const post1 = createTestPost({
        id: "post1",
        publishedDate: "2026-01-15",
      });
      const post2 = createTestPost({
        id: "post2",
        publishedDate: "2026-01-17",
      });
      const post3 = createTestPost({
        id: "post3",
        publishedDate: "2026-01-16",
      });

      const batch = createTestBatch({
        posts: [post1, post2, post3],
      });

      saveBatch(tempDir, batch);

      const posts = loadAllPosts(tempDir);

      assert.equal(posts.length, 3);
      assert.equal(posts[0].id, "post2"); // 2026-01-17
      assert.equal(posts[1].id, "post3"); // 2026-01-16
      assert.equal(posts[2].id, "post1"); // 2026-01-15
    });

    test("should return empty array if no batches exist", () => {
      tempDir = setupTempDir();

      const posts = loadAllPosts(tempDir);

      assert.deepEqual(posts, []);
    });
  });

  describe("listExports", () => {
    test("should list CSV files", () => {
      tempDir = setupTempDir();
      ensureDirectories(tempDir);

      const exportsDir = join(tempDir, "exports");
      writeFileSync(join(exportsDir, "export1.csv"), "data");
      writeFileSync(join(exportsDir, "export2.csv"), "data");
      writeFileSync(join(exportsDir, "other.txt"), "data"); // Non-CSV

      const exports = listExports(tempDir);

      assert.equal(exports.length, 2);
      assert.ok(exports.includes("export1.csv"));
      assert.ok(exports.includes("export2.csv"));
    });

    test("should return empty for missing directory", () => {
      tempDir = setupTempDir();

      const exports = listExports(tempDir);

      assert.deepEqual(exports, []);
    });

    test("should return sorted list", () => {
      tempDir = setupTempDir();
      ensureDirectories(tempDir);

      const exportsDir = join(tempDir, "exports");
      writeFileSync(join(exportsDir, "c.csv"), "data");
      writeFileSync(join(exportsDir, "a.csv"), "data");
      writeFileSync(join(exportsDir, "b.csv"), "data");

      const exports = listExports(tempDir);

      assert.deepEqual(exports, ["a.csv", "b.csv", "c.csv"]);
    });
  });

  describe("saveWeeklyReport", () => {
    test("should write report JSON", () => {
      tempDir = setupTempDir();
      const report = createTestWeeklyReport({ week: "2026-W03" });

      const filename = saveWeeklyReport(tempDir, report);

      assert.equal(filename, "2026-W03.json");

      const filepath = join(tempDir, "weekly-reports", filename);
      assert.ok(existsSync(filepath));

      // Verify content
      const loaded = loadWeeklyReport(tempDir, "2026-W03");
      assert.ok(loaded);
      assert.equal(loaded.week, "2026-W03");
      assert.equal(loaded.summary.totalPosts, 5);
    });

    test("should create directories if they don't exist", () => {
      tempDir = setupTempDir();
      const report = createTestWeeklyReport();

      saveWeeklyReport(tempDir, report);

      assert.ok(existsSync(join(tempDir, "weekly-reports")));
    });
  });

  describe("loadWeeklyReport", () => {
    test("should return null for missing report", () => {
      tempDir = setupTempDir();

      const report = loadWeeklyReport(tempDir, "2026-W99");

      assert.equal(report, null);
    });

    test("should load existing report", () => {
      tempDir = setupTempDir();
      const report = createTestWeeklyReport({ week: "2026-W03" });

      saveWeeklyReport(tempDir, report);

      const loaded = loadWeeklyReport(tempDir, "2026-W03");

      assert.ok(loaded);
      assert.equal(loaded.week, "2026-W03");
      assert.equal(loaded.summary.totalPosts, 5);
    });
  });

  describe("loadAllWeeklyReports", () => {
    test("should load all reports sorted", () => {
      tempDir = setupTempDir();

      const report1 = createTestWeeklyReport({ week: "2026-W03" });
      const report2 = createTestWeeklyReport({ week: "2026-W01" });
      const report3 = createTestWeeklyReport({ week: "2026-W05" });

      saveWeeklyReport(tempDir, report1);
      saveWeeklyReport(tempDir, report2);
      saveWeeklyReport(tempDir, report3);

      const reports = loadAllWeeklyReports(tempDir);

      assert.equal(reports.length, 3);
      assert.equal(reports[0].week, "2026-W05"); // Newest first
      assert.equal(reports[1].week, "2026-W03");
      assert.equal(reports[2].week, "2026-W01");
    });

    test("should return empty array if directory doesn't exist", () => {
      tempDir = setupTempDir();

      const reports = loadAllWeeklyReports(tempDir);

      assert.deepEqual(reports, []);
    });

    test("should ignore non-JSON files", () => {
      tempDir = setupTempDir();
      ensureDirectories(tempDir);

      const reportsDir = join(tempDir, "weekly-reports");
      const report = createTestWeeklyReport({ week: "2026-W03" });
      saveWeeklyReport(tempDir, report);
      writeFileSync(join(reportsDir, "readme.txt"), "data");

      const reports = loadAllWeeklyReports(tempDir);

      assert.equal(reports.length, 1);
      assert.equal(reports[0].week, "2026-W03");
    });
  });
});
