import assert from "node:assert/strict";
import test from "node:test";
import { tokenAssetUploadSchema, verifySignedTokenAsset } from "@pons-agent/protocol";
import { privateKeyToAccount } from "viem/accounts";
import type { AgentConfig } from "../src/config.js";
import { imageModelEndpoint, TokenAssetPublisher } from "../src/token-assets.js";

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 = () => {
  const header = Buffer.alloc(13);
  header.writeUInt32BE(1024, 0);
  header.writeUInt32BE(1024, 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 account = privateKeyToAccount(`0x${"33".repeat(32)}`);
const identity = { name: "Alpha", symbol: "ALPHA", topic: "#alpha" };
const image = pngFixture();
const publicBaseUrl = "https://pons.example";
const config = {
  account,
  publicEvents: { agentId: "test-agent" },
  tokenAssets: {
    endpoint: `${publicBaseUrl}/api/token-assets`,
    publicBaseUrl,
  },
  imageModel: {
    baseUrl: "https://images.example/v1",
    apiKey: "test-image-key",
    name: "gpt-image-2",
  },
} as unknown as AgentConfig;

test("image generation endpoints accept a host, v1 base, or full URL", () => {
  assert.equal(imageModelEndpoint("https://images.example").href, "https://images.example/v1/images/generations");
  assert.equal(imageModelEndpoint("https://images.example/v1").href, "https://images.example/v1/images/generations");
  assert.equal(imageModelEndpoint("https://images.example/v1/images/generations").href, "https://images.example/v1/images/generations");
});

test("automatic token assets generate, sign, upload, and verify a public PNG", async () => {
  const originalFetch = globalThis.fetch;
  let calls = 0;
  globalThis.fetch = (async (_input: string | URL | Request, init?: RequestInit) => {
    calls += 1;
    if (calls === 1) return new Response("not found", { status: 404 });
    if (calls === 2) return Response.json({ data: [{ b64_json: image.toString("base64") }] });
    if (calls === 3) {
      const upload = tokenAssetUploadSchema.parse(JSON.parse(String(init?.body)) as unknown);
      const asset = await verifySignedTokenAsset(upload.asset);
      assert.equal(asset.version, 2);
      assert.equal(upload.imageBase64, image.toString("base64"));
      return Response.json({
        asset,
        assetUrl: `${publicBaseUrl}/api/token-assets/${asset.assetId}`,
        created: true,
      }, { status: 201 });
    }
    return new Response(new Uint8Array(image), { headers: { "content-type": "image/png" } });
  }) as typeof fetch;

  try {
    const url = await new TokenAssetPublisher(config).publish(identity);
    assert.match(url, /^https:\/\/pons\.example\/api\/token-assets\/[a-f0-9]{64}$/);
    assert.equal(calls, 4);
  } finally {
    globalThis.fetch = originalFetch;
  }
});

test("an inaccessible generated PNG blocks the automatic launch path", async () => {
  const originalFetch = globalThis.fetch;
  let calls = 0;
  globalThis.fetch = (async (_input: string | URL | Request, init?: RequestInit) => {
    calls += 1;
    if (calls === 1) return new Response("not found", { status: 404 });
    if (calls === 2) return Response.json({ data: [{ b64_json: image.toString("base64") }] });
    if (calls === 3) {
      const upload = tokenAssetUploadSchema.parse(JSON.parse(String(init?.body)) as unknown);
      return Response.json({
        asset: upload.asset,
        assetUrl: `${publicBaseUrl}/api/token-assets/${upload.asset.assetId}`,
        created: true,
      }, { status: 201 });
    }
    return new Response("unavailable", { status: 503 });
  }) as typeof fetch;

  try {
    await assert.rejects(new TokenAssetPublisher(config).publish(identity), /not publicly accessible/i);
    assert.equal(calls, 4);
  } finally {
    globalThis.fetch = originalFetch;
  }
});

test("an existing valid PNG is reused without another image-model request", async () => {
  const originalFetch = globalThis.fetch;
  let calls = 0;
  globalThis.fetch = (async () => {
    calls += 1;
    return new Response(new Uint8Array(image), { headers: { "content-type": "image/png" } });
  }) as typeof fetch;
  try {
    await new TokenAssetPublisher(config).publish(identity);
    assert.equal(calls, 1);
  } finally {
    globalThis.fetch = originalFetch;
  }
});
