/**
 * ActionState 信封 + 降级模式方案 B 单元测试.
 */
import { describe, it, expect } from "vitest";
import { ok, fail, degraded, DegradedReason, type Degradable } from "./action-state.js";

describe("ActionState", () => {
  describe("ok()", () => {
    it("should construct success response with data only", () => {
      const result = ok({ name: "test" });
      expect(result.success).toBe(true);
      expect(result.data).toEqual({ name: "test" });
      expect(result.meta).toBeUndefined();
    });

    it("should construct success response with meta", () => {
      const result = ok({ count: 1 }, { traceId: "trace-123" });
      expect(result.success).toBe(true);
      expect(result.data).toEqual({ count: 1 });
      expect(result.meta?.traceId).toBe("trace-123");
    });

    it("should include cachedAt in meta", () => {
      const result = ok({ items: [] }, { cachedAt: "2026-07-10T00:00:00Z" });
      expect(result.meta?.cachedAt).toBe("2026-07-10T00:00:00Z");
    });
  });

  describe("fail()", () => {
    it("should construct error response with code and message", () => {
      const result = fail("BFF_STUDENT_NOT_FOUND", "Resource not found");
      expect(result.success).toBe(false);
      expect(result.error.code).toBe("BFF_STUDENT_NOT_FOUND");
      expect(result.error.message).toBe("Resource not found");
    });

    it("should include optional fields", () => {
      const result = fail("BFF_STUDENT_BAD_GATEWAY", "Downstream failed", {
        traceId: "trace-456",
        i18nKey: "error.bffStudent.bad_gateway",
        details: { service: "iam" },
      });
      expect(result.error.traceId).toBe("trace-456");
      expect(result.error.i18nKey).toBe("error.bffStudent.bad_gateway");
      expect(result.error.details).toEqual({ service: "iam" });
    });

    it("should have undefined optional fields when not provided", () => {
      const result = fail("BFF_STUDENT_INTERNAL_ERROR", "Unknown error");
      expect(result.error.traceId).toBeUndefined();
      expect(result.error.i18nKey).toBeUndefined();
      expect(result.error.details).toBeUndefined();
    });
  });

  describe("degraded()", () => {
    it("should construct degraded response with degraded=true", () => {
      interface DashboardData extends Degradable {
        score: number;
      }
      const result = degraded<DashboardData>(
        { score: 90 },
        DegradedReason.DOWNSTREAM_PARTIAL_FAILURE,
        ["weakness", "trend"],
      );
      expect(result.success).toBe(true);
      expect(result.data.score).toBe(90);
      expect(result.data.degraded).toBe(true);
      expect(result.data.degradedReason).toBe("downstream_partial_failure");
      expect(result.data.degradedFields).toEqual(["weakness", "trend"]);
    });

    it("should set degraded=true in meta", () => {
      const result = degraded(
        { items: [] },
        DegradedReason.REDIS_UNAVAILABLE,
        ["items"],
      );
      expect(result.meta?.degraded).toBe(true);
      expect(result.meta?.degradedReason).toBe("redis_unavailable");
    });

    it("should merge additional meta fields", () => {
      const result = degraded(
        { data: "test" },
        DegradedReason.CIRCUIT_OPEN,
        ["data"],
        { traceId: "trace-789" },
      );
      expect(result.meta?.traceId).toBe("trace-789");
      expect(result.meta?.degraded).toBe(true);
    });
  });

  describe("DegradedReason constants", () => {
    it("should export all expected reasons", () => {
      expect(DegradedReason.REDIS_UNAVAILABLE).toBe("redis_unavailable");
      expect(DegradedReason.DOWNSTREAM_PARTIAL_FAILURE).toBe("downstream_partial_failure");
      expect(DegradedReason.DOWNSTREAM_TIMEOUT).toBe("downstream_timeout");
      expect(DegradedReason.CIRCUIT_OPEN).toBe("circuit_open");
      expect(DegradedReason.MOCK_UPSTREAM).toBe("mock_upstream");
    });
  });
});
