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

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";

vi.mock("@/module/apiError.js", () => ({
  default: vi.fn(),
}));

import { useAllMapDataStore } from "@/stores/allMapData";

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

  beforeEach(() => {
    setActivePinia(createPinia());
    store = useAllMapDataStore();
    document.cookie = "luciaToken=fake-test-token";
  });

  it("has correct default state", () => {
    expect(store.logId).toBeNull();
    expect(store.allProcessMap).toEqual({});
    expect(store.allTrace).toEqual([]);
  });

  describe("getAllMapData", () => {
    it("fetches log discover data", async () => {
      store.logId = 1;
      const mockData = {
        process_map: { nodes: [] },
        bpmn: { nodes: [] },
        stats: { cases: 10 },
        insights: {},
      };
      server.use(
        http.get("/api/logs/:id/discover", ({ request }) => {
          captureRequest("GET", new URL(request.url).pathname);
          return HttpResponse.json(mockData);
        }),
      );

      await store.getAllMapData();

      const reqs = findRequest("GET", "/api/logs/1/discover");
      expect(reqs).toHaveLength(1);
      expect(store.allProcessMap).toEqual({ nodes: [] });
      expect(store.allStats).toEqual({ cases: 10 });
    });

    it("fetches temp filter discover data when set", async () => {
      store.logId = 1;
      store.tempFilterId = 5;
      server.use(
        http.get("/api/temp-filters/:id/discover", ({ request }) => {
          captureRequest("GET", new URL(request.url).pathname);
          return HttpResponse.json({
            process_map: {},
            bpmn: {},
            stats: {},
            insights: {},
          });
        }),
      );

      await store.getAllMapData();

      const reqs = findRequest("GET", "/api/temp-filters/5/discover");
      expect(reqs).toHaveLength(1);
    });

    it("fetches created filter discover data", async () => {
      store.logId = 1;
      store.createFilterId = 3;
      server.use(
        http.get("/api/filters/:id/discover", ({ request }) => {
          captureRequest("GET", new URL(request.url).pathname);
          return HttpResponse.json({
            process_map: {},
            bpmn: {},
            stats: {},
            insights: {},
          });
        }),
      );

      await store.getAllMapData();

      const reqs = findRequest("GET", "/api/filters/3/discover");
      expect(reqs).toHaveLength(1);
    });

    it("does not throw on API failure", async () => {
      store.logId = 1;
      server.use(
        http.get("/api/logs/:id/discover", () =>
          new HttpResponse(null, { status: 500 }),
        ),
      );

      await expect(store.getAllMapData()).resolves.not.toThrow();
    });
  });

  describe("getFilterParams", () => {
    it("fetches filter params and transforms timeframe", async () => {
      store.logId = 1;
      const mockData = {
        tasks: ["A", "B"],
        sources: ["A"],
        sinks: ["B"],
        timeframe: {
          x_axis: {
            min: "2023-01-01T00:00:00Z",
            max: "2023-12-31T00:00:00Z",
          },
        },
        trace: [],
        attrs: [],
      };
      server.use(
        http.get("/api/filters/params", ({ request }) => {
          const url = new URL(request.url);
          captureRequest("GET", url.pathname + url.search);
          return HttpResponse.json(mockData);
        }),
      );

      await store.getFilterParams();

      const reqs = findRequest("GET", "/api/filters/params?log_id=1");
      expect(reqs).toHaveLength(1);
      expect(store.allFilterTask).toEqual(["A", "B"]);
      // Check that min_base and max_base are stored
      expect(store.allFilterTimeframe.x_axis.min_base).toBe(
        "2023-01-01T00:00:00Z",
      );
    });
  });

  describe("checkHasResult", () => {
    it("posts rule data and stores result", async () => {
      store.logId = 1;
      store.postRuleData = [{ type: "task" }];
      server.use(
        http.post("/api/filters/has-result", async ({ request }) => {
          const url = new URL(request.url);
          const body = await request.json();
          captureRequest("POST", url.pathname + url.search, body);
          return HttpResponse.json({ result: true });
        }),
      );

      await store.checkHasResult();

      const reqs = findRequest("POST", "/api/filters/has-result?log_id=1");
      expect(reqs).toHaveLength(1);
      expect(reqs[0].body).toEqual([{ type: "task" }]);
      expect(store.hasResultRule).toBe(true);
    });
  });

  describe("addTempFilterId", () => {
    it("creates temp filter and stores id", async () => {
      store.logId = 1;
      store.postRuleData = [];
      server.use(
        http.post("/api/temp-filters", () =>
          HttpResponse.json({ id: 77 }),
        ),
      );

      await store.addTempFilterId();

      expect(store.tempFilterId).toBe(77);
    });
  });

  describe("addFilterId", () => {
    it("creates filter and clears temp id", async () => {
      store.logId = 1;
      store.tempFilterId = 77;
      store.postRuleData = [{ type: "rule" }];
      server.use(
        http.post("/api/filters", async ({ request }) => {
          const url = new URL(request.url);
          const body = await request.json();
          captureRequest("POST", url.pathname + url.search, body);
          return HttpResponse.json({ id: 88 });
        }),
      );

      await store.addFilterId("myFilter");

      const reqs = findRequest("POST", "/api/filters?log_id=1");
      expect(reqs).toHaveLength(1);
      expect(reqs[0].body).toEqual({
        name: "myFilter",
        rules: [{ type: "rule" }],
      });
      expect(store.createFilterId).toBe(88);
      expect(store.tempFilterId).toBeNull();
    });
  });

  describe("updateFilter", () => {
    it("updates filter and clears temp id", async () => {
      store.createFilterId = 88;
      store.tempFilterId = 77;
      store.postRuleData = [{ type: "updated" }];
      server.use(
        http.put("/api/filters/:id", async ({ request, params }) => {
          const body = await request.json();
          captureRequest("PUT", `/api/filters/${params.id}`, body);
          return new HttpResponse(null, { status: 200 });
        }),
      );

      await store.updateFilter();

      const reqs = findRequest("PUT", "/api/filters/88");
      expect(reqs).toHaveLength(1);
      expect(reqs[0].body).toEqual([{ type: "updated" }]);
      expect(store.isUpdateFilter).toBe(true);
      expect(store.tempFilterId).toBeNull();
    });
  });

  describe("getAllTrace", () => {
    it("does not crash when baseLogId is falsy", async () => {
      store.logId = 1;
      store.baseLogId = null;
      server.use(
        http.get("/api/logs/:id/traces", () =>
          HttpResponse.json([{ id: 1 }]),
        ),
      );

      await store.getAllTrace();

      expect(store.allTrace).toEqual([{ id: 1 }]);
      // allBaseTrace should not be set when baseLogId is falsy
      expect(store.allBaseTrace).toEqual([]);
    });
  });

  describe("getters", () => {
    it("traces getter sorts by id", () => {
      store.allTrace = [
        { id: 3, name: "C" },
        { id: 1, name: "A" },
        { id: 2, name: "B" },
      ];
      expect(store.traces.map((t) => t.id)).toEqual([1, 2, 3]);
    });

    it("processMap getter returns state", () => {
      store.allProcessMap = { nodes: [1, 2] };
      expect(store.processMap).toEqual({ nodes: [1, 2] });
    });

    it("BaseInfiniteFirstCases returns allBaseCase when baseInfiniteStart is 0", () => {
      store.baseInfiniteStart = 0;
      store.allBaseCase = [{ id: 1 }, { id: 2 }];
      expect(store.BaseInfiniteFirstCases).toEqual([{ id: 1 }, { id: 2 }]);
    });

    it("BaseInfiniteFirstCases returns undefined when baseInfiniteStart is not 0", () => {
      store.baseInfiniteStart = 5;
      store.allBaseCase = [{ id: 1 }];
      expect(store.BaseInfiniteFirstCases).toBeUndefined();
    });

    it("filterAttrs getter does not corrupt original state on repeated access", () => {
      const originalDate = "2023-06-15T10:30:00Z";
      store.allFilterAttrs = [
        { type: "date", min: originalDate, max: "2023-12-31T23:59:00Z" },
      ];
      // Access the getter once to trigger any side effects
      expect(store.filterAttrs).toBeDefined();
      // The original state should still contain the ISO date, not a formatted string
      expect(store.allFilterAttrs[0].min).toBe(originalDate);
    });

    it("filterAttrs getter returns consistent float values on repeated access", () => {
      store.allFilterAttrs = [{ type: "float", min: 1.239, max: 5.671 }];
      const first = store.filterAttrs;
      const second = store.filterAttrs;
      // min rounds down (ROUND_DOWN=1), max rounds up (ROUND_UP=0)
      // Both accesses should produce the same result
      expect(first[0].min).toBe(second[0].min);
      expect(first[0].max).toBe(second[0].max);
    });
  });
});
