/**
 * Brand Store 单元测试
 *
 * 覆盖：selectBrand / clearSelection / optimisticCreate / optimisticUpdate
 */

import { describe, it, expect, vi, beforeEach } from "vitest";
import { useBrandStore } from "@/lib/stores/brand-store";
import type { BrandListItem } from "@/types/brand";

// ── Mock 依赖 ────────────────────────────────────────────────────────────────

vi.mock("@/lib/api/client", () => ({
  fetchWithAuth: vi.fn(),
}));

vi.mock("@/lib/api/brands", () => ({
  brandsApi: {
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
  },
}));

vi.mock("@/lib/stores/notification-store", () => ({
  useNotificationStore: {
    getState: () => ({
      addNotification: vi.fn(),
    }),
  },
}));

// ── 测试数据 ─────────────────────────────────────────────────────────────────

const MOCK_BRAND: BrandListItem = {
  id: "brand-1",
  name: "测试品牌",
  aliases: ["测试"],
  platforms: ["bing"],
  frequency: "weekly",
  status: "active",
  score: 85,
  last_queried_at: "2024-01-01T00:00:00Z",
  next_query_at: "2024-01-08T00:00:00Z",
  created_at: "2024-01-01T00:00:00Z",
};

const MOCK_BRAND_2: BrandListItem = {
  id: "brand-2",
  name: "另一个品牌",
  aliases: [],
  platforms: ["google"],
  frequency: "daily",
  status: "pending",
  score: null,
  last_queried_at: null,
  next_query_at: null,
  created_at: "2024-01-02T00:00:00Z",
};

// ── 测试 ──────────────────────────────────────────────────────────────────────

describe("useBrandStore", () => {
  beforeEach(() => {
    // 重置 store 到初始状态
    useBrandStore.setState({
      selectedBrandId: null,
      selectedBrandName: null,
      localBrands: [],
      optimisticAction: null,
    });
    vi.clearAllMocks();
  });

  // ── selectBrand / clearSelection ────────────────────────────────────────

  describe("selectBrand / clearSelection", () => {
    it("应正确选中品牌", () => {
      const { selectBrand } = useBrandStore.getState();
      selectBrand("brand-1", "测试品牌");

      const state = useBrandStore.getState();
      expect(state.selectedBrandId).toBe("brand-1");
      expect(state.selectedBrandName).toBe("测试品牌");
    });

    it("选中品牌时 name 参数可选", () => {
      const { selectBrand } = useBrandStore.getState();
      selectBrand("brand-2");

      const state = useBrandStore.getState();
      expect(state.selectedBrandId).toBe("brand-2");
      expect(state.selectedBrandName).toBeNull();
    });

    it("应正确清除选中品牌", () => {
      useBrandStore.setState({
        selectedBrandId: "brand-1",
        selectedBrandName: "测试品牌",
      });

      const { clearSelection } = useBrandStore.getState();
      clearSelection();

      const state = useBrandStore.getState();
      expect(state.selectedBrandId).toBeNull();
      expect(state.selectedBrandName).toBeNull();
    });

    it("切换选中品牌应覆盖旧值", () => {
      useBrandStore.setState({
        selectedBrandId: "brand-1",
        selectedBrandName: "旧品牌",
      });

      const { selectBrand } = useBrandStore.getState();
      selectBrand("brand-2", "新品牌");

      const state = useBrandStore.getState();
      expect(state.selectedBrandId).toBe("brand-2");
      expect(state.selectedBrandName).toBe("新品牌");
    });
  });

  // ── syncFromSWR ─────────────────────────────────────────────────────────

  describe("syncFromSWR", () => {
    it("应同步 SWR 数据到本地副本", () => {
      const { syncFromSWR } = useBrandStore.getState();
      syncFromSWR([MOCK_BRAND, MOCK_BRAND_2]);

      const state = useBrandStore.getState();
      expect(state.localBrands).toHaveLength(2);
      expect(state.localBrands[0].id).toBe("brand-1");
      expect(state.localBrands[1].id).toBe("brand-2");
    });

    it("多次同步应替换而非追加", () => {
      const { syncFromSWR } = useBrandStore.getState();
      syncFromSWR([MOCK_BRAND]);
      syncFromSWR([MOCK_BRAND_2]);

      const state = useBrandStore.getState();
      expect(state.localBrands).toHaveLength(1);
      expect(state.localBrands[0].id).toBe("brand-2");
    });
  });

  // ── optimisticCreate ────────────────────────────────────────────────────

  describe("optimisticCreate", () => {
    it("成功时应在本地添加临时条目后替换为真实数据", async () => {
      const { brandsApi } = await import("@/lib/api/brands");
      const created = vi.mocked(brandsApi.create).mockResolvedValueOnce(
        MOCK_BRAND as never
      );

      const swrMutate = vi.fn();
      const { optimisticCreate } = useBrandStore.getState();

      const result = await optimisticCreate(
        "test-token",
        { name: "测试品牌", platforms: ["bing"] },
        swrMutate
      );

      // 返回值应为 API 创建结果
      expect(result).toEqual(MOCK_BRAND);

      // 乐观操作状态已清除
      expect(useBrandStore.getState().optimisticAction).toBeNull();

      // 本地列表应包含真实数据（非临时 ID）
      const brands = useBrandStore.getState().localBrands;
      expect(brands).toHaveLength(1);
      expect(brands[0].id).toBe("brand-1");

      // SWR mutate 应被调用
      expect(swrMutate).toHaveBeenCalled();
    });

    it("失败时应回滚本地临时条目", async () => {
      const { brandsApi } = await import("@/lib/api/brands");
      vi.mocked(brandsApi.create).mockRejectedValueOnce(
        new Error("创建失败") as never
      );

      const swrMutate = vi.fn();
      const { optimisticCreate } = useBrandStore.getState();

      const result = await optimisticCreate(
        "test-token",
        { name: "失败品牌" },
        swrMutate
      );

      // 返回 null
      expect(result).toBeNull();

      // 本地列表应回滚（为空）
      expect(useBrandStore.getState().localBrands).toHaveLength(0);

      // 乐观操作状态已清除
      expect(useBrandStore.getState().optimisticAction).toBeNull();

      // SWR mutate 不应被调用
      expect(swrMutate).not.toHaveBeenCalled();
    });

    it("创建过程中 optimisticAction 应为 creating", async () => {
      const { brandsApi } = await import("@/lib/api/brands");
      let resolveCreate: (value: unknown) => void;
      const createPromise = new Promise((resolve) => {
        resolveCreate = resolve;
      });
      vi.mocked(brandsApi.create).mockReturnValueOnce(
        createPromise as never
      );

      const { optimisticCreate } = useBrandStore.getState();
      const createResult = optimisticCreate("test-token", { name: "异步品牌" });

      // API 调用期间
      expect(useBrandStore.getState().optimisticAction).toBe("creating");
      expect(useBrandStore.getState().localBrands).toHaveLength(1);
      expect(useBrandStore.getState().localBrands[0].name).toBe("异步品牌");

      // 解析 API
      resolveCreate!(MOCK_BRAND);
      await createResult;

      expect(useBrandStore.getState().optimisticAction).toBeNull();
    });
  });

  // ── optimisticUpdate ────────────────────────────────────────────────────

  describe("optimisticUpdate", () => {
    beforeEach(() => {
      useBrandStore.setState({
        localBrands: [MOCK_BRAND, MOCK_BRAND_2],
      });
    });

    it("成功时应在本地乐观更新后替换为 API 返回数据", async () => {
      const updatedBrand: BrandListItem = {
        ...MOCK_BRAND,
        name: "更新后品牌",
      };

      const { brandsApi } = await import("@/lib/api/brands");
      vi.mocked(brandsApi.update).mockResolvedValueOnce(
        updatedBrand as never
      );

      const swrMutate = vi.fn();
      const { optimisticUpdate } = useBrandStore.getState();

      const result = await optimisticUpdate(
        "test-token",
        "brand-1",
        { name: "更新后品牌" },
        swrMutate
      );

      expect(result).toEqual(updatedBrand);
      expect(useBrandStore.getState().optimisticAction).toBeNull();

      // 本地列表应包含更新后的数据
      const brands = useBrandStore.getState().localBrands;
      expect(brands.find((b) => b.id === "brand-1")?.name).toBe("更新后品牌");

      expect(swrMutate).toHaveBeenCalled();
    });

    it("失败时应回滚到原始数据", async () => {
      const { brandsApi } = await import("@/lib/api/brands");
      vi.mocked(brandsApi.update).mockRejectedValueOnce(
        new Error("更新失败") as never
      );

      const swrMutate = vi.fn();
      const { optimisticUpdate } = useBrandStore.getState();

      const result = await optimisticUpdate(
        "test-token",
        "brand-1",
        { name: "回滚品牌" },
        swrMutate
      );

      expect(result).toBeNull();
      expect(useBrandStore.getState().optimisticAction).toBeNull();

      // 本地列表应回滚到原始数据
      const brands = useBrandStore.getState().localBrands;
      expect(brands.find((b) => b.id === "brand-1")?.name).toBe("测试品牌");

      expect(swrMutate).not.toHaveBeenCalled();
    });

    it("本地无品牌时直接走 API", async () => {
      useBrandStore.setState({ localBrands: [] });

      const updatedBrand: BrandListItem = { ...MOCK_BRAND, name: "新品牌" };
      const { brandsApi } = await import("@/lib/api/brands");
      vi.mocked(brandsApi.update).mockResolvedValueOnce(
        updatedBrand as never
      );

      const swrMutate = vi.fn();
      const { optimisticUpdate } = useBrandStore.getState();

      const result = await optimisticUpdate(
        "test-token",
        "brand-1",
        { name: "新品牌" },
        swrMutate
      );

      expect(result).toEqual(updatedBrand);
      expect(swrMutate).toHaveBeenCalled();
    });

    it("更新过程中 optimisticAction 应为 updating", async () => {
      const { brandsApi } = await import("@/lib/api/brands");
      let resolveUpdate: (value: unknown) => void;
      const updatePromise = new Promise((resolve) => {
        resolveUpdate = resolve;
      });
      vi.mocked(brandsApi.update).mockReturnValueOnce(
        updatePromise as never
      );

      const { optimisticUpdate } = useBrandStore.getState();
      const updateResult = optimisticUpdate(
        "test-token",
        "brand-1",
        { name: "异步更新" }
      );

      // API 调用期间
      expect(useBrandStore.getState().optimisticAction).toBe("updating");

      // 本地应已乐观更新
      expect(
        useBrandStore.getState().localBrands.find((b) => b.id === "brand-1")?.name
      ).toBe("异步更新");

      resolveUpdate!({ ...MOCK_BRAND, name: "异步更新" });
      await updateResult;

      expect(useBrandStore.getState().optimisticAction).toBeNull();
    });
  });

  // ── optimisticDelete ────────────────────────────────────────────────────

  describe("optimisticDelete", () => {
    beforeEach(() => {
      useBrandStore.setState({
        localBrands: [MOCK_BRAND, MOCK_BRAND_2],
        selectedBrandId: null,
        selectedBrandName: null,
      });
    });

    it("成功时应在本地移除品牌", async () => {
      const { brandsApi } = await import("@/lib/api/brands");
      vi.mocked(brandsApi.delete).mockResolvedValueOnce(undefined as never);

      const swrMutate = vi.fn();
      const { optimisticDelete } = useBrandStore.getState();

      const result = await optimisticDelete("test-token", "brand-1", swrMutate);

      expect(result).toBe(true);
      expect(useBrandStore.getState().optimisticAction).toBeNull();

      // 本地列表不再包含被删除品牌
      const brands = useBrandStore.getState().localBrands;
      expect(brands).toHaveLength(1);
      expect(brands.find((b) => b.id === "brand-1")).toBeUndefined();

      expect(swrMutate).toHaveBeenCalled();
    });

    it("失败时应恢复被删除的品牌", async () => {
      const { brandsApi } = await import("@/lib/api/brands");
      vi.mocked(brandsApi.delete).mockRejectedValueOnce(
        new Error("删除失败") as never
      );

      const swrMutate = vi.fn();
      const { optimisticDelete } = useBrandStore.getState();

      const result = await optimisticDelete("test-token", "brand-1", swrMutate);

      expect(result).toBe(false);
      expect(useBrandStore.getState().optimisticAction).toBeNull();

      // 本地列表应恢复被删除品牌
      const brands = useBrandStore.getState().localBrands;
      expect(brands).toHaveLength(2);
      expect(brands.find((b) => b.id === "brand-1")).toBeDefined();
    });

    it("删除当前选中品牌时应清除选择", async () => {
      useBrandStore.setState({
        selectedBrandId: "brand-1",
        selectedBrandName: "测试品牌",
      });

      const { brandsApi } = await import("@/lib/api/brands");
      vi.mocked(brandsApi.delete).mockResolvedValueOnce(undefined as never);

      const { optimisticDelete } = useBrandStore.getState();
      await optimisticDelete("test-token", "brand-1");

      expect(useBrandStore.getState().selectedBrandId).toBeNull();
      expect(useBrandStore.getState().selectedBrandName).toBeNull();
    });

    it("删除非选中品牌时不应影响选择", async () => {
      useBrandStore.setState({
        selectedBrandId: "brand-1",
        selectedBrandName: "测试品牌",
      });

      const { brandsApi } = await import("@/lib/api/brands");
      vi.mocked(brandsApi.delete).mockResolvedValueOnce(undefined as never);

      const { optimisticDelete } = useBrandStore.getState();
      await optimisticDelete("test-token", "brand-2");

      expect(useBrandStore.getState().selectedBrandId).toBe("brand-1");
      expect(useBrandStore.getState().selectedBrandName).toBe("测试品牌");
    });
  });
});
