import { ADVANCED_SETTINGS_VERSION } from "@sdp/issuance/capabilities";
import type { IssuanceMetadata } from "@sdp/types";
import { describe, expect, it } from "vitest";
import {
  getSelectedSettings,
  resolveAdvancedSettings,
  selectedAuthorityValuedSettings,
  stampAdvancedSettingsVersion,
  validateAdvancedSettings,
} from "./advanced-settings";

describe("advanced settings persistence helpers", () => {
  describe("validateAdvancedSettings", () => {
    it("accepts settings allowed for the asset type", () => {
      const metadata: IssuanceMetadata = {
        settings: { selected: { pauseTransfers: {}, permanentDelegate: {} } },
      };
      expect(validateAdvancedSettings("stablecoin", "fiat_backed", metadata)).toEqual([]);
    });

    it("rejects an unsupported setting for the asset type", () => {
      const metadata: IssuanceMetadata = {
        settings: { selected: { nonTransferable: {} } },
      };
      expect(validateAdvancedSettings("stablecoin", "fiat_backed", metadata)).toEqual([
        { settingKey: "nonTransferable", reason: "unsupported" },
      ]);
    });

    it("rejects an unknown setting key", () => {
      const metadata: IssuanceMetadata = { settings: { selected: { made_up: {} } } };
      expect(validateAdvancedSettings("generic", "generic", metadata)).toEqual([
        { settingKey: "made_up", reason: "unknown" },
      ]);
    });

    it("accepts a retired key rather than reporting it as unknown", () => {
      // Clients and stored profiles predating the freezeTransfers split must not
      // start failing validation.
      const metadata: IssuanceMetadata = { settings: { selected: { freezeTransfers: {} } } };
      expect(validateAdvancedSettings("stablecoin", "fiat_backed", metadata)).toEqual([]);
    });

    it("returns no errors when there is no settings namespace", () => {
      expect(validateAdvancedSettings("generic", "generic", { asset: { name: "X" } })).toEqual([]);
      expect(validateAdvancedSettings("generic", "generic", {})).toEqual([]);
    });

    it("rejects an out-of-range numeric param on an otherwise-supported setting", () => {
      // transferFee is allowed for generic assets, so the key check passes; the
      // catalog bound (basisPoints ∈ [0, 10_000]) is what rejects 99999. This is
      // the hole a crafted payload would otherwise use — the client min/max is
      // advisory only.
      const metadata: IssuanceMetadata = {
        settings: { selected: { transferFee: { params: { basisPoints: 99999 } } } },
      };
      expect(validateAdvancedSettings("generic", "generic", metadata)).toEqual([
        { settingKey: "transferFee", paramKey: "basisPoints", reason: "above_max", limit: 10_000 },
      ]);
    });

    it("accepts an in-range numeric param", () => {
      const metadata: IssuanceMetadata = {
        settings: { selected: { transferFee: { params: { basisPoints: 250, maxFee: "0" } } } },
      };
      expect(validateAdvancedSettings("generic", "generic", metadata)).toEqual([]);
    });

    it("rejects a malformed string param before it reaches the Solana layer", () => {
      // transferHook.programId is a base58 pubkey; an arbitrary string clears the zod
      // shape check but must be rejected here rather than fail opaquely at deploy.
      const metadata: IssuanceMetadata = {
        settings: { selected: { transferHook: { params: { programId: "not-a-pubkey" } } } },
      };
      expect(validateAdvancedSettings("generic", "generic", metadata)).toEqual([
        { settingKey: "transferHook", paramKey: "programId", reason: "invalid_format" },
      ]);
    });

    it("does not re-flag params of a setting already rejected at the key level", () => {
      // transferFee is unsupported on a stablecoin: report the key once, and skip
      // its param bounds rather than piling on a second (redundant) error.
      const metadata: IssuanceMetadata = {
        settings: { selected: { transferFee: { params: { basisPoints: 99999 } } } },
      };
      expect(validateAdvancedSettings("stablecoin", "fiat_backed", metadata)).toEqual([
        { settingKey: "transferFee", reason: "unsupported" },
      ]);
    });
  });

  describe("stampAdvancedSettingsVersion", () => {
    it("stamps the server version onto a selection", () => {
      const metadata: IssuanceMetadata = { settings: { selected: { pauseTransfers: {} } } };
      const stamped = stampAdvancedSettingsVersion(metadata);
      expect((stamped.settings as { version: number }).version).toBe(ADVANCED_SETTINGS_VERSION);
      // input is not mutated
      expect((metadata.settings as { version?: number }).version).toBeUndefined();
    });

    it("overwrites a client-supplied version with the server version", () => {
      const metadata: IssuanceMetadata = {
        settings: { version: 999, selected: { pauseTransfers: {} } },
      };
      const stamped = stampAdvancedSettingsVersion(metadata);
      expect((stamped.settings as { version: number }).version).toBe(ADVANCED_SETTINGS_VERSION);
    });

    it("is a no-op when there is no settings selection", () => {
      const metadata: IssuanceMetadata = { asset: { name: "X" } };
      expect(stampAdvancedSettingsVersion(metadata)).toBe(metadata);
    });
  });

  describe("getSelectedSettings", () => {
    it("returns the selected settings map", () => {
      const metadata: IssuanceMetadata = {
        settings: { selected: { pauseTransfers: {}, permanentDelegate: {} } },
      };
      expect(getSelectedSettings(metadata)).toEqual({ pauseTransfers: {}, permanentDelegate: {} });
    });

    it("returns an empty object when there is no selection", () => {
      expect(getSelectedSettings({ asset: { name: "X" } })).toEqual({});
      expect(getSelectedSettings({})).toEqual({});
    });

    it("rewrites a retired key stored before the catalog split", () => {
      // Profiles persisted as "freezeTransfers" must keep working: it covered both
      // the pausable extension and the base mint's freeze authority, which are now
      // separate settings.
      const metadata: IssuanceMetadata = { settings: { selected: { freezeTransfers: {} } } };
      expect(getSelectedSettings(metadata)).toEqual({
        pauseTransfers: {},
        freezeAccounts: {},
      });
    });
  });

  describe("selectedAuthorityValuedSettings", () => {
    it("flags permanentDelegate — its authority needs a real signing wallet", () => {
      // create.ts rejects this when no signer resolves, so the resolver never emits a
      // placeholder authority into an immutable extension.
      const metadata: IssuanceMetadata = {
        settings: { selected: { pauseTransfers: {}, permanentDelegate: {} } },
      };
      expect(selectedAuthorityValuedSettings(metadata)).toEqual(["permanentDelegate"]);
    });

    it("returns empty for selections with no authority-valued setting", () => {
      const metadata: IssuanceMetadata = {
        settings: {
          selected: { pauseTransfers: {}, transferFee: { params: { basisPoints: 10 } } },
        },
      };
      expect(selectedAuthorityValuedSettings(metadata)).toEqual([]);
    });

    it("returns empty when there is no selection", () => {
      expect(selectedAuthorityValuedSettings({ asset: { name: "X" } })).toEqual([]);
      expect(selectedAuthorityValuedSettings({})).toEqual([]);
    });
  });

  describe("resolveAdvancedSettings", () => {
    it("returns no errors when the selection builds against the template", () => {
      const metadata: IssuanceMetadata = { settings: { selected: { pauseTransfers: {} } } };
      expect(resolveAdvancedSettings("stablecoin", "fiat_backed", metadata)).toEqual([]);
    });

    it("returns no errors when there is no settings namespace", () => {
      expect(resolveAdvancedSettings("generic", "generic", { asset: { name: "X" } })).toEqual([]);
    });

    it("surfaces a template error for a selection the substrate can't build", () => {
      // transferFee bypassing the capability check → the stablecoin template rejects it.
      const metadata: IssuanceMetadata = {
        settings: { selected: { transferFee: { params: { basisPoints: 10, maxFee: "1" } } } },
      };
      const errors = resolveAdvancedSettings("stablecoin", "fiat_backed", metadata);
      expect(errors.length).toBeGreaterThan(0);
      expect(errors[0].code).toBe("EXTENSION_NOT_ALLOWED");
    });
  });
});
