/** Minimal protobuf + gRPC-web framing for cart_hamster.cart.CartService/AddProduct */

function writeVarint(n: number): Buffer {
  const bytes: number[] = [];
  let value = Math.floor(n);
  if (value < 0) value = 0;
  while (value > 0x7f) {
    bytes.push((value & 0x7f) | 0x80);
    value >>>= 7;
  }
  bytes.push(value);
  return Buffer.from(bytes);
}

function writeTag(fieldNumber: number, wireType: number): Buffer {
  return writeVarint((fieldNumber << 3) | wireType);
}

function writeStringField(fieldNumber: number, value: string): Buffer {
  const utf8 = Buffer.from(value, 'utf8');
  return Buffer.concat([writeTag(fieldNumber, 2), writeVarint(utf8.length), utf8]);
}

function writeInt64Field(fieldNumber: number, value: number): Buffer {
  return Buffer.concat([writeTag(fieldNumber, 0), writeVarint(value)]);
}

function encodeProductToAdd(productId: number, qty: number): Buffer {
  const parts: Buffer[] = [];
  if (productId) parts.push(writeInt64Field(1, productId));
  if (qty) parts.push(writeInt64Field(3, qty));
  return Buffer.concat(parts);
}

export function encodeAddProductRequest(
  products: Array<{ productId: number; qty: number }>,
  opts?: { sessionId?: string; country?: string; lang?: string },
): Buffer {
  const out: Buffer[] = [
    writeStringField(1, opts?.sessionId ?? ''),
    writeStringField(2, opts?.country ?? 'PL'),
    writeStringField(3, opts?.lang ?? 'pl'),
  ];
  for (const p of products) {
    const msg = encodeProductToAdd(p.productId, p.qty);
    out.push(Buffer.concat([writeTag(4, 2), writeVarint(msg.length), msg]));
  }
  return Buffer.concat(out);
}

export function encodeCartGetRequest(opts?: {
  sessionId?: string;
  country?: string;
  lang?: string;
}): Buffer {
  return Buffer.concat([
    writeStringField(1, opts?.sessionId ?? ''),
    writeStringField(2, opts?.country ?? 'PL'),
    writeStringField(3, opts?.lang ?? 'pl'),
  ]);
}

export function grpcWebRequestFrame(message: Buffer): Buffer {
  const frame = Buffer.alloc(5 + message.length);
  frame[0] = 0;
  frame.writeUInt32BE(message.length, 1);
  message.copy(frame, 5);
  return frame;
}

/** Sinsay cart uses grpc-web `format: "text"` (application/grpc-web-text). */
export function grpcWebTextBody(message: Buffer): string {
  return grpcWebRequestFrame(message).toString('base64');
}

function decodeGrpcWebTextBody(body: Buffer): Buffer {
  const trimmed = body.toString('utf8').trim();
  if (!trimmed || /^[\x00-\x08]/.test(trimmed.slice(0, 1))) return body;
  try {
    return Buffer.from(trimmed, 'base64');
  } catch {
    return body;
  }
}

/** Walk protobuf bytes and scale wire-type-1 doubles that look like PLN prices. */
export function patchProtobufPriceDoubles(
  buf: Buffer,
  start: number,
  end: number,
  factor: number,
): void {
  let i = start;
  while (i < end) {
    const tag = buf[i++];
    if (tag === 0) break;
    const wire = tag & 7;
    const fieldNum = tag >> 3;
    if (fieldNum === 0) break;

    if (wire === 0) {
      while (i < end && buf[i] & 0x80) i++;
      i++;
    } else if (wire === 1) {
      if (i + 8 > end) return;
      const val = buf.readDoubleLE(i);
      if (val >= 0.5 && val <= 50_000 && Math.abs(val - Math.round(val)) > 0.001) {
        buf.writeDoubleLE(Math.round(val * factor * 100) / 100, i);
      }
      i += 8;
    } else if (wire === 2) {
      let len = 0;
      let shift = 0;
      while (i < end) {
        const b = buf[i++];
        len |= (b & 0x7f) << shift;
        if (!(b & 0x80)) break;
        shift += 7;
      }
      const nestedEnd = i + len;
      if (nestedEnd > end) return;
      patchProtobufPriceDoubles(buf, i, nestedEnd, factor);
      i = nestedEnd;
    } else if (wire === 5) {
      i += 4;
    } else {
      break;
    }
  }
}

export function patchGrpcWebCartPrices(body: Buffer, discountPercent: number): Buffer {
  if (discountPercent <= 0 || discountPercent >= 100) return body;
  const factor = (100 - discountPercent) / 100;
  const decoded = decodeGrpcWebTextBody(body);
  const wasText = decoded !== body;
  const out = Buffer.from(decoded);
  let offset = 0;
  while (offset + 5 <= out.length) {
    const flag = out[offset];
    if (flag & 0x80) break;
    const len = out.readUInt32BE(offset + 1);
    const start = offset + 5;
    const end = start + len;
    if (end > out.length) break;
    patchProtobufPriceDoubles(out, start, end, factor);
    offset = end;
  }
  return wasText ? Buffer.from(out.toString('base64'), 'utf8') : out;
}

/** Magento `frontend` cookie value → cart protobuf sessionid (before comma). */
export function sinsayCartSessionId(cookies: Record<string, string | undefined>): string {
  const raw = String(cookies.frontend ?? cookies.Frontend ?? '').trim();
  if (!raw) return '';
  return raw.split(',')[0]?.trim() ?? '';
}
