import assert from "node:assert/strict";
import test from "node:test";
import { privateKeyToAccount } from "viem/accounts";
import {
  eventSigningMessage,
  generatedUnsignedTokenAssetSchema,
  imageContentHash,
  renderTokenLogoSvg,
  stableJson,
  tokenAssetId,
  tokenAssetSigningMessage,
  unsignedTokenAssetSchema,
  validatePngImage,
  verifySignedAgentEvent,
  verifySignedTokenAsset,
  type UnsignedAgentEvent,
} from "../src/index.js";

const account = privateKeyToAccount(`0x${"11".repeat(32)}`);

const uint32 = (value: number) => {
  const bytes = Buffer.alloc(4);
  bytes.writeUInt32BE(value);
  return bytes;
};

const pngChunk = (type: string, data = Buffer.alloc(0)) => Buffer.concat([
  uint32(data.length),
  Buffer.from(type, "ascii"),
  data,
  Buffer.alloc(4),
]);

const pngFixture = (width = 1024, height = 1024) => {
  const header = Buffer.alloc(13);
  header.writeUInt32BE(width, 0);
  header.writeUInt32BE(height, 4);
  header.set([8, 2, 0, 0, 0], 8);
  return Buffer.concat([
    Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
    pngChunk("IHDR", header),
    pngChunk("IDAT", Buffer.from([1])),
    pngChunk("IEND"),
  ]);
};

const event: UnsignedAgentEvent = {
  version: 1,
  eventId: "4c0b4cb2-7966-4e0f-873b-0de18ece1e9b",
  agentId: "public-agent",
  sequence: 7,
  type: "trend.detected",
  status: "observed",
  occurredAt: "2026-07-22T00:00:00.000Z",
  chainId: 4663,
  payload: { score: 91, topic: "test", nested: { beta: 2, alpha: 1 } },
};

test("stableJson produces the same output regardless of key order", () => {
  assert.equal(stableJson({ beta: 2, alpha: 1 }), stableJson({ alpha: 1, beta: 2 }));
});

test("signed events verify and payload tampering fails", async () => {
  const signature = await account.signMessage({ message: eventSigningMessage(event) });
  const signed = { ...event, signer: account.address, signature };
  const verified = await verifySignedAgentEvent(signed);
  assert.equal(verified.signer, account.address);

  await assert.rejects(
    verifySignedAgentEvent({ ...signed, payload: { ...event.payload, score: 1 } }),
    /signature does not match/i,
  );
});

test("legacy Pons event signatures remain verifiable", async () => {
  const legacyMessage = eventSigningMessage(event).replace("zoi-agent-event:v1", "pons-agent-event:v1");
  const signature = await account.signMessage({ message: legacyMessage });
  const verified = await verifySignedAgentEvent({ ...event, signer: account.address, signature });
  assert.equal(verified.eventId, event.eventId);
});

test("token logos are deterministic, unique, and escape untrusted text", () => {
  const alpha = { name: "Alpha & <script>", symbol: "ALPHA", topic: "#alpha" };
  const same = { topic: "#alpha", symbol: "ALPHA", name: "Alpha & <script>" };
  const beta = { ...alpha, topic: "#beta" };

  assert.equal(tokenAssetId(alpha), tokenAssetId(same));
  assert.notEqual(tokenAssetId(alpha), tokenAssetId(beta));
  assert.equal(renderTokenLogoSvg(alpha), renderTokenLogoSvg(same));
  assert.match(renderTokenLogoSvg(alpha), /Alpha &amp; &lt;script&gt;/);
  assert.doesNotMatch(renderTokenLogoSvg(alpha), /<script>/);
});

test("signed token assets verify and identity tampering fails", async () => {
  const identity = { name: "Alpha", symbol: "ALPHA", topic: "#alpha" };
  const unsigned = unsignedTokenAssetSchema.parse({
    version: 1,
    assetId: tokenAssetId(identity),
    agentId: "public-agent",
    createdAt: "2026-07-22T00:00:00.000Z",
    chainId: 4663,
    ...identity,
  });
  const signature = await account.signMessage({ message: tokenAssetSigningMessage(unsigned) });
  const signed = { ...unsigned, signer: account.address, signature };

  assert.equal((await verifySignedTokenAsset(signed)).assetId, unsigned.assetId);
  await assert.rejects(
    verifySignedTokenAsset({ ...signed, topic: "#tampered" }),
    /asset ID does not match/i,
  );
});

test("generated PNG metadata is signed and invalid dimensions are rejected", async () => {
  const identity = { name: "Image Alpha", symbol: "IMALPHA", topic: "#image-alpha" };
  const image = pngFixture();
  assert.deepEqual(validatePngImage(image), { width: 1024, height: 1024 });
  assert.throws(() => validatePngImage(pngFixture(512, 0)), /dimensions are invalid/i);

  const unsigned = generatedUnsignedTokenAssetSchema.parse({
    version: 2,
    assetId: tokenAssetId(identity),
    agentId: "public-agent",
    createdAt: "2026-07-22T00:00:00.000Z",
    chainId: 4663,
    ...identity,
    media: {
      mimeType: "image/png",
      contentHash: imageContentHash(image),
      byteLength: image.length,
      width: 1024,
      height: 1024,
      model: "gpt-image-2",
    },
  });
  const signature = await account.signMessage({ message: tokenAssetSigningMessage(unsigned) });
  const signed = { ...unsigned, signer: account.address, signature };
  assert.equal((await verifySignedTokenAsset(signed)).version, 2);
  await assert.rejects(
    verifySignedTokenAsset({ ...signed, media: { ...signed.media, contentHash: "00".repeat(32) } }),
    /signature does not match/i,
  );
});
