import { create } from "@bufbuild/protobuf";
import * as Protobuf from "@meshtastic/protobufs";
import { describe, expect, it } from "vitest";
import { MeshClient } from "../../core/client/MeshClient.ts";
import { createFakeTransport } from "../../core/testing/createFakeTransport.ts";
import { ChannelNumber } from "../../core/types.ts";

const MY_NODE = 0xdeadbeef;

function dispatchInbound(
  client: MeshClient,
  opts: {
    from: number;
    to?: number;
    channel?: ChannelNumber;
    type?: "direct" | "broadcast";
    ms: number;
    text?: string;
  },
): void {
  client.events.onMessagePacket.dispatch({
    id: opts.ms,
    from: opts.from,
    to: opts.to ?? 0,
    channel: opts.channel ?? ChannelNumber.Primary,
    type: opts.type ?? "broadcast",
    rxTime: new Date(opts.ms),
    data: opts.text ?? "hi",
  } as never);
}

function setMyNode(client: MeshClient): void {
  client.events.onMyNodeInfo.dispatch(
    create(Protobuf.Mesh.MyNodeInfoSchema, { myNodeNum: MY_NODE }),
  );
}

describe("ChatClient unread counts", () => {
  it("starts at zero for every conversation", () => {
    const { transport } = createFakeTransport();
    const client = new MeshClient({ transport });
    expect(client.chat.unread.total.value).toBe(0);
    expect(
      client.chat.unread.count({
        kind: "channel",
        channel: ChannelNumber.Primary,
      }).value,
    ).toBe(0);
  });

  it("increments on inbound broadcast and decrements on markRead", () => {
    const { transport } = createFakeTransport();
    const client = new MeshClient({ transport });
    setMyNode(client);

    dispatchInbound(client, { from: 100, ms: 1000 });
    dispatchInbound(client, { from: 200, ms: 2000 });

    expect(
      client.chat.unread.count({
        kind: "channel",
        channel: ChannelNumber.Primary,
      }).value,
    ).toBe(2);
    expect(client.chat.unread.total.value).toBe(2);

    client.chat.unread.markRead({
      kind: "channel",
      channel: ChannelNumber.Primary,
    });
    expect(client.chat.unread.total.value).toBe(0);
  });

  it("does not increment for outbound echoes (from === myNodeNum)", () => {
    const { transport } = createFakeTransport();
    const client = new MeshClient({ transport });
    setMyNode(client);

    dispatchInbound(client, { from: MY_NODE, ms: 1000 });
    expect(client.chat.unread.total.value).toBe(0);
  });

  it("keys direct messages by peer, not channel", () => {
    const { transport } = createFakeTransport();
    const client = new MeshClient({ transport });
    setMyNode(client);

    dispatchInbound(client, {
      from: 50,
      to: MY_NODE,
      type: "direct",
      ms: 1000,
    });
    expect(client.chat.unread.count({ kind: "direct", peer: 50 }).value).toBe(
      1,
    );
    expect(
      client.chat.unread.count({
        kind: "channel",
        channel: ChannelNumber.Primary,
      }).value,
    ).toBe(0);
  });
});
