import { randomUUID } from 'crypto';

export type MessageNode = {
  __typename?: string;
  id?: string;
  time?: number;
  userInput?: string;
  attachmentInput?: string;
  status?: string;
  author?: {
    __typename?: string;
    appId?: string;
    userId?: string;
    metadata?: Record<string, unknown>;
  };
  payload?: { __typename?: string; value?: string; translated?: string | null };
};

/** Visitor messages in the PWA widget use this appId (see Zowie signIn mutation). */
export const ZOWIE_CUSTOMER_APP_ID = 'herochat-pwa';

/** Zowie AI / bot authors — anything except customer PWA and our manager panel. */
export function isZowieAiAuthor(appId: unknown): boolean {
  if (typeof appId !== 'string' || !appId) return false;
  if (appId === ZOWIE_CUSTOMER_APP_ID || appId === 'agentpanel') return false;
  return true;
}

export function isBlockedZowieMessage(msg: unknown): boolean {
  if (!msg || typeof msg !== 'object') return false;
  const node = msg as MessageNode;
  const id = node.id;
  // Keep messages injected by our bridge
  if (typeof id === 'string' && id.startsWith('proxy-')) return false;
  if (isZowieAiAuthor(node.author?.appId)) return true;
  return false;
}

/** Zowie enables the composer when the latest message has userInput === "Enabled". */
export function withComposerEnabled<T extends Record<string, unknown>>(message: T): T {
  return {
    ...message,
    userInput: 'Enabled',
    attachmentInput: 'Enabled',
    status: 'Sent',
  };
}

export function buildZowieCustomerMessage(text: string): MessageNode {
  return withComposerEnabled({
    __typename: 'Message',
    id: `proxy-user-${randomUUID()}`,
    time: Date.now(),
    author: {
      __typename: 'Author',
      appId: ZOWIE_CUSTOMER_APP_ID,
      userId: 'visitor',
      metadata: {
        __typename: 'AuthorMetadata',
        firstName: null,
        lastName: null,
        profilePictureUrl: null,
      },
    },
    payload: {
      __typename: 'Text',
      value: text,
      translated: null,
    },
  });
}

export function buildBlockedMutationResponse(
  field: 'sendText' | 'sendButton',
  text: string,
): string {
  return JSON.stringify({
    data: {
      [field]: buildZowieCustomerMessage(text),
    },
  });
}

export function parseBlockedUserMutation(body: unknown): {
  field: 'sendText' | 'sendButton';
  text: string;
  conversationId?: string;
} | null {
  if (!body || typeof body !== 'object') return null;
  const obj = body as { query?: string; variables?: Record<string, unknown> };
  if (!obj.query || typeof obj.query !== 'string') return null;

  const q = obj.query.toLowerCase();
  let field: 'sendText' | 'sendButton' | null = null;
  if (q.includes('sendtext')) field = 'sendText';
  else if (q.includes('sendbutton')) field = 'sendButton';
  else return null;

  const vars = obj.variables;
  if (!vars || typeof vars !== 'object') return null;

  const text =
    (typeof vars.text === 'string' ? vars.text : '') ||
    (typeof vars.buttonText === 'string' ? vars.buttonText : '') ||
    (typeof vars.label === 'string' ? vars.label : '') ||
    (typeof vars.title === 'string' ? vars.title : '');
  const trimmed = text.trim();
  if (!trimmed) return null;

  return {
    field,
    text: trimmed,
    conversationId:
      typeof vars.conversationId === 'string' ? vars.conversationId : undefined,
  };
}

function filterMessageEdges(edges: unknown[]): unknown[] {
  return edges.filter((edge) => {
    if (!edge || typeof edge !== 'object') return true;
    const node = (edge as { node?: unknown }).node;
    return !isBlockedZowieMessage(node);
  });
}

function filterMessagesInValue(value: unknown): unknown {
  if (Array.isArray(value)) {
    return value.map(filterMessagesInValue);
  }
  if (!value || typeof value !== 'object') return value;

  const obj = value as Record<string, unknown>;
  const out: Record<string, unknown> = {};

  for (const [key, val] of Object.entries(obj)) {
    if (key === 'edges' && Array.isArray(val)) {
      out[key] = filterMessageEdges(val);
      continue;
    }
    if (key === 'newMessage' && isBlockedZowieMessage(val)) {
      continue;
    }
    out[key] = filterMessagesInValue(val);
  }

  return out;
}

/** Strip AI/bot messages from Zowie GraphQL HTTP JSON responses (history, etc.). */
export function filterGraphqlResponseBody(raw: Buffer): Buffer {
  const text = raw.toString('utf8').trim();
  if (!text.startsWith('{')) return raw;

  try {
    const parsed = JSON.parse(text) as unknown;
    const filtered = filterMessagesInValue(parsed);
    return Buffer.from(JSON.stringify(filtered), 'utf8');
  } catch {
    return raw;
  }
}

const WS_PUSH_TYPES = new Set(['data', 'next', 'subscription_data']);

/** Return false to drop an upstream GraphQL-WS frame before it reaches the browser. */
export function shouldForwardUpstreamWsFrame(raw: string | Buffer): boolean {
  const text = typeof raw === 'string' ? raw : raw.toString('utf8');
  try {
    const msg = JSON.parse(text) as {
      type?: string;
      payload?: { data?: Record<string, unknown> };
    };
    if (!msg.type || !WS_PUSH_TYPES.has(msg.type) || !msg.payload?.data) return true;

    for (const value of Object.values(msg.payload.data)) {
      if (isBlockedZowieMessage(value)) return false;
    }
    return true;
  } catch {
    return true;
  }
}
