import { z } from "zod";
import {
  ChatActionSchema,
  CommercialActionSchema,
  PaymentGuidanceSchema,
} from "@/data/schemas/chat";

export const UiMessageSchema = z.object({
  displayId: z.string().min(1),
  remoteId: z.string().min(1).optional(),
  clientId: z.string().min(1).optional(),
  content: z.string(),
  isFromAI: z.boolean(),
  date: z.string(),
  isSynthetic: z.boolean().optional(),
  imageUrl: z.string().optional(),
  imagePaywalled: z.boolean().optional(),
  audioUrl: z.string().optional(),
  locked: z.boolean().nullable().optional(),
  lockReason: z.string().nullable().optional(),
  isPrivate: z.boolean().nullable().optional(),
  lockedPrivate: z.boolean().nullable().optional(),
  privateMessageHint: z.string().nullable().optional(),
  commercialAction: CommercialActionSchema.nullable().optional(),
  chatAction: ChatActionSchema.nullable().optional(),
  paymentGuidance: PaymentGuidanceSchema.nullable().optional(),
});

export type UiMessage = z.infer<typeof UiMessageSchema>;

let fallbackClientId = 0;

export function createRemoteUiMessageIdentity(
  remoteId: string,
  isFromAI: boolean,
  occurrence = 0,
): Pick<UiMessage, "displayId" | "remoteId"> {
  const role = isFromAI ? "assistant" : "user";
  const base = `server:${encodeURIComponent(remoteId)}:${role}`;
  return {
    displayId: occurrence > 0 ? `${base}:${occurrence}` : base,
    remoteId,
  };
}

export function createLegacyUiMessageIdentity(
  fingerprint: string,
  occurrence = 0,
): Pick<UiMessage, "displayId"> {
  const base = `legacy:${hashIdentity(fingerprint)}`;
  return {
    displayId: occurrence > 0 ? `${base}:${occurrence}` : base,
  };
}

export function createClientUiMessageIdentity(
  kind: "message" | "image" | "reply" | "error",
): Pick<UiMessage, "displayId" | "clientId"> {
  const clientId = createClientId();
  return {
    clientId,
    displayId: `client:${kind}:${clientId}`,
  };
}

export const UiMessage = {
  create(input: Omit<UiMessage, "date"> & { date?: string }): UiMessage {
    return UiMessageSchema.parse({
      ...input,
      date:
        input.date ??
        new Date().toLocaleTimeString([], {
          hour: "2-digit",
          minute: "2-digit",
        }),
    });
  },
};

function createClientId(): string {
  if (typeof globalThis.crypto?.randomUUID === "function") {
    return globalThis.crypto.randomUUID();
  }
  fallbackClientId += 1;
  return `${Date.now().toString(36)}-${fallbackClientId.toString(36)}`;
}

function hashIdentity(value: string): string {
  let hash = 0x811c9dc5;
  for (let index = 0; index < value.length; index += 1) {
    hash ^= value.charCodeAt(index);
    hash = Math.imul(hash, 0x01000193);
  }
  return (hash >>> 0).toString(36);
}
