/**
 * Protobuf wire schemas used by this module for encode/decode helpers.
 *
 * The `*Schema` consts below are exported strictly for backwards
 * compatibility with external consumers that deep-imported them
 * before `@origintrail-official/dkg-core` had an `exports` map.
 * They are implementation detail — prefer the `*Msg` types and
 * `encode*` / `decode*` functions re-exported from
 * `packages/core/src/proto/index.ts`.
 *
 * @internal
 */
import protobuf from 'protobufjs';

const { Type, Field } = protobuf;

/** Manifest entry for one root entity in a shared memory write (no tokenId). */
export const WorkspaceManifestEntrySchema = new Type('WorkspaceManifestEntry')
  .add(new Field('rootEntity', 1, 'string'))
  .add(new Field('privateMerkleRoot', 2, 'bytes'))
  .add(new Field('privateTripleCount', 3, 'uint32'));

/** CAS condition carried in gossip so receiving peers enforce the same guard. */
export const WorkspaceCASConditionSchema = new Type('WorkspaceCASCondition')
  .add(new Field('subject', 1, 'string'))
  .add(new Field('predicate', 2, 'string'))
  .add(new Field('expectedValue', 3, 'string'))
  .add(new Field('expectAbsent', 4, 'bool'));

export const WorkspacePublishRequestSchema = new Type('WorkspacePublishRequest')
  .add(new Field('contextGraphId', 1, 'string'))
  .add(new Field('nquads', 2, 'bytes'))
  .add(new Field('manifest', 3, 'WorkspaceManifestEntry', 'repeated'))
  .add(new Field('publisherPeerId', 4, 'string'))
  .add(new Field('shareOperationId', 5, 'string'))
  .add(new Field('timestampMs', 6, 'uint64'))
  .add(new Field('operationId', 7, 'string'))
  .add(new Field('casConditions', 8, 'WorkspaceCASCondition', 'repeated'))
  .add(new Field('subGraphName', 9, 'string'))
  .add(WorkspaceManifestEntrySchema)
  .add(WorkspaceCASConditionSchema);

export interface WorkspaceManifestEntryMsg {
  rootEntity: string;
  privateMerkleRoot?: Uint8Array;
  privateTripleCount?: number;
}

export interface WorkspaceCASConditionMsg {
  subject: string;
  predicate: string;
  /** Expected RDF term, or empty string when expectAbsent is true. */
  expectedValue: string;
  /** If true, the triple (subject, predicate, *) must not exist. */
  expectAbsent: boolean;
}

export interface WorkspacePublishRequestMsg {
  contextGraphId: string;
  nquads: Uint8Array;
  manifest: WorkspaceManifestEntryMsg[];
  publisherPeerId: string;
  shareOperationId: string;
  timestampMs: number | bigint;
  /** Originator's operation ID for cross-node log correlation. */
  operationId?: string;
  /** CAS conditions that receiving peers must enforce before applying this write. */
  casConditions?: WorkspaceCASConditionMsg[];
  /** Sub-graph within the context graph. Receivers store in sub-graph SWM if set. */
  subGraphName?: string;
}

export function encodeWorkspacePublishRequest(msg: WorkspacePublishRequestMsg): Uint8Array {
  if (!String(msg.shareOperationId ?? '').trim()) {
    throw new Error('WorkspacePublishRequest requires shareOperationId');
  }
  const ts = typeof msg.timestampMs === 'bigint' ? Number(msg.timestampMs) : msg.timestampMs;
  return WorkspacePublishRequestSchema.encode(
    WorkspacePublishRequestSchema.create({ ...msg, timestampMs: ts }),
  ).finish();
}

export function decodeWorkspacePublishRequest(buf: Uint8Array): WorkspacePublishRequestMsg {
  const decoded = WorkspacePublishRequestSchema.decode(buf) as unknown as WorkspacePublishRequestMsg;
  const ts = decoded.timestampMs;
  return {
    ...decoded,
    timestampMs: typeof ts === 'bigint' ? Number(ts) : (ts as number),
  };
}

// ── V10 aliases (Workspace → SharedMemory / Share) ──────────────────────
export type ShareManifestEntryMsg = WorkspaceManifestEntryMsg;
export type ShareCASConditionMsg = WorkspaceCASConditionMsg;
export type SharePublishRequestMsg = WorkspacePublishRequestMsg;
export const encodeSharePublishRequest = encodeWorkspacePublishRequest;
export const decodeSharePublishRequest = decodeWorkspacePublishRequest;
