import { createRoute } from "../../modules/api-utils/createRoute";
import { apiResponse } from "../../modules/api-utils/apiResponse";
import { ContentfulStatusCode } from "hono/utils/http-status";
import { z } from "zod";
import GenericError from "../../Errors/GenericError";

type ParsedJson = Record<string, unknown> | unknown[];

const callbackResource = z.enum([
  "opportunity",
  "ticket",
  "company",
  "activity",
]);

const safeParseJson = (value: string): ParsedJson | null => {
  try {
    const parsed = JSON.parse(value);
    const isObject = typeof parsed === "object" && parsed !== null;

    return isObject ? (parsed as ParsedJson) : null;
  } catch {
    return null;
  }
};

const asObject = (value: ParsedJson | null): Record<string, unknown> | null => {
  if (!value) return null;
  if (Array.isArray(value)) return null;

  return value;
};

const parseJsonStringFields = (
  value: Record<string, unknown> | null,
): Record<string, unknown> | null => {
  if (!value) return null;

  return Object.entries(value).reduce<Record<string, unknown>>(
    (acc, [key, current]) => {
      if (typeof current !== "string") {
        acc[key] = current;

        return acc;
      }

      const looksLikeJson = current.startsWith("{") || current.startsWith("[");
      if (!looksLikeJson) {
        acc[key] = current;

        return acc;
      }

      const parsed = safeParseJson(current);
      acc[key] = parsed ?? current;

      return acc;
    },
    {},
  );
};

const parseEntity = (value: unknown): ParsedJson | null => {
  if (typeof value === "string") return safeParseJson(value);
  if (typeof value !== "object" || value === null) return null;

  return value as ParsedJson;
};

const buildSummary = (
  resource: z.infer<typeof callbackResource>,
  parsedBody: Record<string, unknown> | null,
  parsedEntity: Record<string, unknown> | null,
) => {
  if (!parsedBody) return null;

  return {
    resource,
    messageId: parsedBody.MessageId ?? null,
    action: parsedBody.Action ?? null,
    type: parsedBody.Type ?? null,
    id: parsedBody.ID ?? null,
    memberId: parsedBody.MemberId ?? null,
    entityStatus:
      parsedEntity?.StatusName ??
      parsedEntity?.TicketStatus ??
      parsedEntity?.Status ??
      null,
    entitySummary: parsedEntity?.Summary ?? parsedEntity?.CompanyName ?? null,
    entityUpdatedBy: parsedEntity?.UpdatedBy ?? null,
    entityLastUpdated:
      parsedEntity?.LastUpdatedUTC ?? parsedEntity?.LastUpdated ?? null,
  };
};

const parseHeaders = (headers: Headers): Record<string, string> =>
  Object.fromEntries(headers.entries());

const callbackHeaderSummary = (headers: Record<string, string>) => ({
  contentType: headers["content-type"] ?? null,
  userAgent: headers["user-agent"] ?? null,
  host: headers.host ?? null,
  forwardedFor: headers["x-forwarded-for"] ?? null,
  callbackId:
    headers["x-cw-request-id"] ??
    headers["x-request-id"] ??
    headers["x-correlation-id"] ??
    null,
});

/* /v1/cw/callback/:resource */
export default createRoute("post", ["/callback/:secret/:resource"], async (c) => {
  const suppliedSecret = c.req.param("secret");
  const expectedSecret = process.env.CW_CALLBACK_SECRET;

  if (expectedSecret && suppliedSecret !== expectedSecret) {
    throw new GenericError({
      name: "Unauthorized",
      message: "Invalid callback secret.",
      cause: "Path secret mismatch",
      status: 401,
    });
  }

  if (!expectedSecret) {
    console.warn(
      "[cw-callback] CW_CALLBACK_SECRET is not configured; accepting path secret without verification",
    );
  }

  const resource = callbackResource.parse(c.req.param("resource"));
  const headers = parseHeaders(c.req.raw.headers);
  const headerSummary = callbackHeaderSummary(headers);
  const rawBody = await c.req.text();
  const parsedJson = safeParseJson(rawBody);
  const parsedBody = asObject(parsedJson);
  const parsedBodyExpanded = parseJsonStringFields(parsedBody);
  const parsedEntity = asObject(parseEntity(parsedBodyExpanded?.Entity));
  const summary = buildSummary(resource, parsedBodyExpanded, parsedEntity);

  const line = [
    `[cw-callback] resource=${resource}`,
    `action=${String(summary?.action ?? "-")}`,
    `type=${String(summary?.type ?? "-")}`,
    `id=${String(summary?.id ?? "-")}`,
    `by=${String(summary?.entityUpdatedBy ?? summary?.memberId ?? "-")}`,
    `requestId=${String(headerSummary.callbackId ?? "-")}`,
    `status=${String(summary?.entityStatus ?? "-")}`,
    `summary=${String(summary?.entitySummary ?? "-")}`,
  ].join(" ");
  console.log(line);

  const response = apiResponse.successful("CW callback received.", {
    resource,
    secretValidated: Boolean(expectedSecret),
    summary,
    headers,
    headerSummary,
    bodyParsed: parsedBodyExpanded,
    receivedAt: new Date().toISOString(),
  });

  return c.json(response, response.status as ContentfulStatusCode);
});
