// The Lucia project.
// Copyright 2026-2026 DSP, inc.  All rights reserved.
// Authors:
//   imacat.yang@dsp.im (imacat), 2026/03/06

import { describe, it, expect, beforeEach, vi } from "vitest";
import { setActivePinia, createPinia } from "pinia";
import { http, HttpResponse } from "msw";
import { server } from "@/mocks/node.js";
import { findRequest, captureRequest } from "@/mocks/request-log.js";

// Mock modules that have deep import chains (router, Swal, pinia, toast)
vi.mock("@/module/apiError.js", () => ({
  default: vi.fn(),
}));
vi.mock("@/module/alertModal.js", () => ({
  uploadFailedFirst: vi.fn(),
  uploadFailedSecond: vi.fn(),
  uploadloader: vi.fn(),
  uploadSuccess: vi.fn(),
  deleteSuccess: vi.fn(),
}));
vi.mock("sweetalert2", () => ({
  default: { close: vi.fn(), fire: vi.fn() },
}));
// Prevent module-level store init in cytoscapeMap.js (loaded via router → Map.vue)
vi.mock("@/module/cytoscapeMap.js", () => ({}));
vi.mock("@/router/index.ts", () => ({
  default: { push: vi.fn(), currentRoute: { value: { path: "/" } } },
}));

import { useFilesStore } from "@/stores/files";

describe("filesStore", () => {
  let store;

  beforeEach(() => {
    setActivePinia(createPinia());
    store = useFilesStore();
    store.$router = { push: vi.fn() };
    document.cookie = "luciaToken=fake-test-token";
  });

  it("has correct default state", () => {
    expect(store.filesTag).toBe("ALL");
    expect(store.httpStatus).toBe(200);
    expect(store.uploadId).toBeNull();
  });

  describe("allFiles getter", () => {
    it("filters files by current filesTag", () => {
      store.allEventFiles = [
        { fileType: "Log", name: "a.xes" },
        { fileType: "Filter", name: "b" },
        { fileType: "Design", name: "c" },
      ];

      store.filesTag = "COMPARE";
      expect(store.allFiles.map((f) => f.name)).toEqual(["a.xes", "b"]);

      store.filesTag = "ALL";
      expect(store.allFiles).toHaveLength(3);
    });
  });

  describe("fetchAllFiles", () => {
    it("fetches and transforms file data", async () => {
      server.use(
        http.get("/api/files", ({ request }) => {
          captureRequest("GET", new URL(request.url).pathname);
          return HttpResponse.json([
            {
              type: "log",
              name: "test.xes",
              owner: { name: "Alice" },
              updated_at: "2024-01-15T10:00:00Z",
              accessed_at: "2024-01-15T11:00:00Z",
            },
            {
              type: "filter",
              name: "filter1",
              parent: { name: "test.xes" },
              owner: { name: "Bob" },
              updated_at: "2024-01-16T10:00:00Z",
              accessed_at: null,
            },
          ]);
        }),
      );

      await store.fetchAllFiles();

      const reqs = findRequest("GET", "/api/files");
      expect(reqs).toHaveLength(1);
      expect(store.allEventFiles).toHaveLength(2);
      expect(store.allEventFiles[0].fileType).toBe("Log");
      expect(store.allEventFiles[0].icon).toBe("work_history");
      expect(store.allEventFiles[0].ownerName).toBe("Alice");
      expect(store.allEventFiles[1].fileType).toBe("Filter");
      expect(store.allEventFiles[1].parentLog).toBe("test.xes");
      expect(store.allEventFiles[1].accessed_at).toBeNull();
    });

    it("does not throw on API failure", async () => {
      server.use(
        http.get("/api/files", () =>
          new HttpResponse(null, { status: 500 }),
        ),
      );
      await expect(store.fetchAllFiles()).resolves.toBeUndefined();
    });

    it("maps design files without leaking metadata from previous file items", async () => {
      server.use(
        http.get("/api/files", () =>
          HttpResponse.json([
            {
              type: "log",
              name: "order-log",
              owner: { name: "Alice" },
              updated_at: "2024-01-15T10:00:00Z",
              accessed_at: null,
            },
            {
              type: "design",
              name: "diagram-a",
              owner: { name: "Bob" },
              updated_at: "2024-01-16T10:00:00Z",
              accessed_at: null,
            },
          ]),
        ),
      );

      await store.fetchAllFiles();

      expect(store.allEventFiles[1].icon).toBe("shape_line");
      expect(store.allEventFiles[1].fileType).toBe("Design");
      expect(store.allEventFiles[1].parentLog).toBe("diagram-a");
    });
  });

  describe("upload", () => {
    it("uploads file and navigates to Upload page", async () => {
      server.use(
        http.post("/api/logs/csv-uploads", async ({ request }) => {
          captureRequest("POST", new URL(request.url).pathname);
          return HttpResponse.json({ id: 42 });
        }),
      );
      const formData = new FormData();

      await store.upload(formData);

      const reqs = findRequest("POST", "/api/logs/csv-uploads");
      expect(reqs).toHaveLength(1);
      expect(store.uploadId).toBe(42);
      expect(store.$router.push).toHaveBeenCalledWith({ name: "Upload" });
    });
  });

  describe("getUploadDetail", () => {
    it("fetches upload preview", async () => {
      store.uploadId = 10;
      server.use(
        http.get("/api/logs/csv-uploads/:id", ({ request }) => {
          captureRequest("GET", new URL(request.url).pathname);
          return HttpResponse.json({ preview: { columns: ["a", "b"] } });
        }),
      );

      await store.getUploadDetail();

      const reqs = findRequest("GET", "/api/logs/csv-uploads/10");
      expect(reqs).toHaveLength(1);
      expect(store.allUploadDetail).toEqual({ columns: ["a", "b"] });
    });
  });

  describe("rename", () => {
    it("renames a log file", async () => {
      server.use(
        http.put("/api/logs/:id/name", async ({ request, params }) => {
          const body = await request.json();
          captureRequest("PUT", `/api/logs/${params.id}/name`, body);
          return HttpResponse.json({});
        }),
        http.get("/api/files", () => HttpResponse.json([])),
      );

      await store.rename("log", 5, "new-name");

      const reqs = findRequest("PUT", "/api/logs/5/name");
      expect(reqs).toHaveLength(1);
      expect(reqs[0].body).toEqual({ name: "new-name" });
    });
  });

  describe("getDependents", () => {
    it("fetches dependents for a log", async () => {
      server.use(
        http.get("/api/logs/:id/dependents", ({ request }) => {
          captureRequest("GET", new URL(request.url).pathname);
          return HttpResponse.json([{ id: 1 }, { id: 2 }]);
        }),
      );

      await store.getDependents("log", 7);

      const reqs = findRequest("GET", "/api/logs/7/dependents");
      expect(reqs).toHaveLength(1);
      expect(store.allDependentsData).toEqual([{ id: 1 }, { id: 2 }]);
    });
  });

  describe("deleteFile", () => {
    it("calls delete before fetchAllFiles", async () => {
      const callOrder = [];
      server.use(
        http.delete("/api/logs/:id", ({ params }) => {
          callOrder.push("delete");
          captureRequest("DELETE", `/api/logs/${params.id}`);
          return HttpResponse.json({});
        }),
        http.get("/api/files", () => {
          callOrder.push("get");
          return HttpResponse.json([]);
        }),
      );

      await store.deleteFile("log", 1);

      const reqs = findRequest("DELETE", "/api/logs/1");
      expect(reqs).toHaveLength(1);
      expect(callOrder.indexOf("delete")).toBeLessThan(
        callOrder.indexOf("get"),
      );
    });

    it("returns early for invalid id without throwing", async () => {
      const spy = vi.spyOn(console, "error").mockImplementation(() => {});

      await expect(store.deleteFile("log", null)).resolves.toBeUndefined();

      expect(spy).toHaveBeenCalledWith("Delete File API Error: invalid id");
      spy.mockRestore();
    });
  });

  describe("deletionRecord", () => {
    it("deletes a deletion record", async () => {
      server.use(
        http.delete("/api/deletion/:id", ({ params }) => {
          captureRequest("DELETE", `/api/deletion/${params.id}`);
          return HttpResponse.json({});
        }),
      );

      await store.deletionRecord(5);

      const reqs = findRequest("DELETE", "/api/deletion/5");
      expect(reqs).toHaveLength(1);
    });
  });

  describe("downloadFileCSV", () => {
    it("downloads CSV for a log", async () => {
      server.use(
        http.get("/api/logs/:id/csv", ({ request }) => {
          captureRequest("GET", new URL(request.url).pathname);
          return new HttpResponse("col1,col2\na,b");
        }),
      );

      globalThis.URL.createObjectURL = vi.fn().mockReturnValue("blob:test");
      globalThis.URL.revokeObjectURL = vi.fn();

      await store.downloadFileCSV("log", 3, "my-file");

      const reqs = findRequest("GET", "/api/logs/3/csv");
      expect(reqs).toHaveLength(1);
      expect(globalThis.URL.revokeObjectURL).toHaveBeenCalledWith("blob:test");
    });

    it("returns early for unsupported type", async () => {
      await store.downloadFileCSV("log-check", 3, "file");

      const reqs = findRequest("GET", /\/api\/.*\/csv/);
      expect(reqs).toHaveLength(0);
    });
  });
});
