import { FastifyReply, FastifyRequest } from 'fastify';
import { Dispatcher, request as undiciRequest } from 'undici';

import { processResponseHeaders } from '../proxy/headers';

const UPSTREAM_ORIGIN = 'https://wishlist-api.sinsay.com';
const UPSTREAM_HOST = 'wishlist-api.sinsay.com';

function bodyToBuffer(body: unknown): Buffer | undefined {
  if (body == null) return undefined;
  if (Buffer.isBuffer(body)) return body.length ? body : undefined;
  if (typeof body === 'string') return body.length ? Buffer.from(body, 'utf8') : undefined;
  if (body instanceof Uint8Array) return Buffer.from(body);
  try {
    return Buffer.from(JSON.stringify(body), 'utf8');
  } catch {
    return undefined;
  }
}

function buildWishlistHeaders(
  incomingHeaders: Record<string, string | string[] | undefined>,
): Record<string, string> {
  const headers: Record<string, string> = {};

  for (const [name, value] of Object.entries(incomingHeaders)) {
    const key = name.toLowerCase();
    if (
      key === 'host' ||
      key === 'connection' ||
      key === 'content-length' ||
      key === 'accept-encoding' ||
      key === 'origin' ||
      key === 'referer'
    ) {
      continue;
    }
    if (value !== undefined) headers[key] = Array.isArray(value) ? value.join(', ') : value;
  }

  headers.host = UPSTREAM_HOST;
  headers.origin = 'https://www.sinsay.com';
  headers.referer = 'https://www.sinsay.com/pl/pl/';
  headers['accept-encoding'] = 'identity';
  return headers;
}

export async function handleWishlistApiProxy(
  request: FastifyRequest<{ Params: { '*': string } }>,
  reply: FastifyReply,
): Promise<void> {
  const apiPath = request.params['*'] ?? '';
  const query = request.url.includes('?') ? request.url.slice(request.url.indexOf('?')) : '';
  const upstreamUrl = `${UPSTREAM_ORIGIN}/${apiPath}${query}`;

  const upstream = await undiciRequest(upstreamUrl, {
    method: request.method as Dispatcher.HttpMethod,
    headers: buildWishlistHeaders(request.headers as Record<string, string | string[] | undefined>),
    body: bodyToBuffer(request.body),
    throwOnError: false,
  });

  const chunks: Buffer[] = [];
  for await (const chunk of upstream.body) {
    chunks.push(chunk instanceof Buffer ? chunk : Buffer.from(chunk));
  }

  const responseHeaders = processResponseHeaders(
    upstream.headers as Record<string, string | string[]>,
  );
  for (const [key, value] of Object.entries(responseHeaders)) {
    reply.header(key, value as string);
  }

  reply.status(upstream.statusCode).send(Buffer.concat(chunks));
}
