import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { z } from "zod";
import { writeAuditLog } from "../../../lib/audit.js";
import { requireSession } from "../../../lib/auth.js";
import {
  countKommoConversationsNeedingAttention,
  countKommoFailedDeliveries,
  countKommoRetryScheduledDeliveries,
  collectKommoLeadAddEvents,
  getKommoWorkspaceSettings,
  collectKommoMessageAddEvents,
  getKommoLeadStats,
  ingestKommoSalesbotMessage,
  ingestKommoWebhookMessage,
  importKommoLeads,
  linkKommoLeadToClient,
  listKommoLeads,
  parseKommoFormBody,
  probeKommoLeadApi,
  resolveAuthorizedKommoLawFirm,
  saveKommoWorkspaceSettings,
  syncKommoLead,
  verifyKommoWidgetRequestToken,
} from "../../../lib/kommo.js";
import { getSessionProfile } from "../../../lib/session.js";

const workspaceParamsSchema = z.object({
  workspaceKey: z.string().min(1),
});

const webhookQuerySchema = z.object({
  secret: z.string().optional(),
  workspaceKey: z.string().optional(),
});

const workspaceQuerySchema = z.object({
  workspaceKey: z.string().min(1).optional(),
});

const secretQuerySchema = z.object({
  secret: z.string().optional(),
});

const kommoLeadListQuerySchema = z.object({
  status: z.enum(["all", "linked", "unlinked"]).optional().default("all"),
  limit: z.coerce.number().int().min(1).max(200).optional(),
});

const kommoLeadImportBodySchema = z.object({
  limit: z.coerce.number().int().min(1).max(5000).optional(),
});

const kommoSettingsBodySchema = z.object({
  subdomain: z.string().min(1).max(120),
  accessToken: z.string().max(4096).optional().or(z.literal("")),
  webhookSecret: z.string().max(512).optional().or(z.literal("")),
  integrationSecretKey: z.string().max(512).optional().or(z.literal("")),
});

const kommoLeadParamsSchema = z.object({
  leadRecordId: z.string().uuid(),
});

const kommoLeadLinkBodySchema = z.object({
  clientId: z.string().uuid(),
});

function getKommoWebhookSecret(input: {
  headers: Record<string, string | string[] | undefined>;
  query: unknown;
}) {
  const query = webhookQuerySchema.safeParse(input.query);
  const headerValue = input.headers["x-kommo-secret"];

  if (typeof headerValue === "string" && headerValue.trim()) {
    return headerValue.trim();
  }

  if (Array.isArray(headerValue) && headerValue[0]?.trim()) {
    return headerValue[0].trim();
  }

  return query.success ? query.data.secret ?? null : null;
}

function getKommoWorkspaceKey(input: {
  params: unknown;
  query: unknown;
  headers: Record<string, string | string[] | undefined>;
}) {
  const params = workspaceParamsSchema.safeParse(input.params);

  if (params.success && params.data.workspaceKey.trim()) {
    return params.data.workspaceKey.trim();
  }

  const query = workspaceQuerySchema.safeParse(input.query);

  if (query.success && query.data.workspaceKey?.trim()) {
    return query.data.workspaceKey.trim();
  }

  const headerValue = input.headers["x-workspace-key"];

  if (typeof headerValue === "string" && headerValue.trim()) {
    return headerValue.trim();
  }

  if (Array.isArray(headerValue) && headerValue[0]?.trim()) {
    return headerValue[0].trim();
  }

  return null;
}

function normalizePayload(body: unknown) {
  return body && typeof body === "object" && !Array.isArray(body)
    ? (body as Record<string, unknown>)
    : {};
}

function getRequestOrigin(request: FastifyRequest) {
  const forwardedProto = request.headers["x-forwarded-proto"];
  const forwardedHost = request.headers["x-forwarded-host"];
  const protocol =
    typeof forwardedProto === "string" && forwardedProto.trim()
      ? forwardedProto.split(",")[0]?.trim() || request.protocol
      : request.protocol;
  const host =
    typeof forwardedHost === "string" && forwardedHost.trim()
      ? forwardedHost.split(",")[0]?.trim() || request.headers.host || ""
      : request.headers.host || "";

  return `${protocol}://${host}`;
}

function installKommoFormParser(app: FastifyInstance) {
  app.addContentTypeParser(
    /^application\/x-www-form-urlencoded(?:;.*)?$/i,
    { parseAs: "string" },
    (_request, body, done) => {
      try {
        done(null, parseKommoFormBody(String(body)));
      } catch (error) {
        done(error as Error);
      }
    },
  );
}

async function buildKommoIntegrationSummary(input: {
  request: FastifyRequest;
  lawFirmId: string;
  workspaceKey: string;
}) {
  const origin = getRequestOrigin(input.request);
  const leadStats = await getKommoLeadStats(input.lawFirmId);
  const [retryScheduledCount, failedDeliveryCount, conversationsNeedingAttentionCount, settings, apiProbe] =
    await Promise.all([
      countKommoRetryScheduledDeliveries(input.lawFirmId),
      countKommoFailedDeliveries(input.lawFirmId),
      countKommoConversationsNeedingAttention(input.lawFirmId),
      getKommoWorkspaceSettings(input.lawFirmId),
      probeKommoLeadApi({ lawFirmId: input.lawFirmId }),
    ]);
  const webhookSecret = settings?.webhookSecret?.trim() ?? "";
  const integrationSecretKey = settings?.integrationSecretKey?.trim() ?? "";

  return {
    workspaceKey: input.workspaceKey,
    subdomain: settings?.subdomain ?? null,
    apiConfigured: Boolean(settings?.subdomain && settings?.accessToken),
    accessTokenConfigured: Boolean(settings?.accessToken),
    accessTokenLast4: settings?.accessTokenLast4 ?? null,
    webhookSecretConfigured: Boolean(webhookSecret),
    salesbotSecretConfigured: Boolean(integrationSecretKey),
    salesbotSecretKeyLast4: settings?.integrationSecretKeyLast4 ?? null,
    webhookUrl: `${origin}/webhook/kommo?workspaceKey=${encodeURIComponent(input.workspaceKey)}`,
    salesbotWebhookUrl: `${origin}/webhook/kommo/salesbot/messages?workspaceKey=${encodeURIComponent(input.workspaceKey)}`,
    webhookSecretHeaderName: "x-kommo-secret",
    webhookSecretQueryParam: "secret",
    webhookSecret: webhookSecret || null,
    leadStats,
    apiProbe: {
      configured: apiProbe.configured,
      checkedAt: apiProbe.checkedAt.toISOString(),
      requestPath: apiProbe.requestPath,
      reachable: apiProbe.reachable,
      sampleLeadCount: apiProbe.sampleLeadCount,
      nextPageAvailable: apiProbe.nextPageAvailable,
      sampleLeadIds: apiProbe.sampleLeadIds,
      sampleLeadNames: apiProbe.sampleLeadNames,
      errorMessage: apiProbe.errorMessage,
    },
    outboundHealth: {
      retryScheduledCount,
      failedDeliveryCount,
      conversationsNeedingAttentionCount,
    },
    behavior: {
      requiresManualLeadLink: true,
      mirrorsInboundMessagesToRepositoryAfterLink: true,
      outboundRepliesUseSalesbotReturnUrl: true,
    },
  };
}

async function handleKommoWebhook(request: FastifyRequest, reply: FastifyReply) {
  const workspaceKey = getKommoWorkspaceKey({
    params: request.params,
    query: request.query,
    headers: request.headers,
  });

  if (!workspaceKey) {
    throw reply.badRequest("Informe o workspaceKey do Kommo por query ou header.");
  }

  const lawFirm = await resolveAuthorizedKommoLawFirm({
    workspaceKey,
    providedSecret: getKommoWebhookSecret({
      headers: request.headers,
      query: request.query,
    }),
  });

  if (!lawFirm) {
    throw reply.notFound("Workspace não encontrado para a integração Kommo.");
  }

  const payload = normalizePayload(request.body);
  const leadEvents = collectKommoLeadAddEvents(payload);
  const messageEvents = collectKommoMessageAddEvents(payload);
  const syncedLeadIds: string[] = [];
  let syncedMessagesCount = 0;

  for (const leadEvent of leadEvents) {
    const leadId = String(leadEvent.id ?? "").trim();

    if (!leadId) {
      continue;
    }

    await syncKommoLead({
      lawFirmId: lawFirm.id,
      leadId,
      lead: leadEvent as { id: string; name?: string | null; custom_fields?: unknown },
    });
    syncedLeadIds.push(leadId);
  }

  for (const messageEvent of messageEvents) {
    const result = await ingestKommoWebhookMessage({
      lawFirmId: lawFirm.id,
      message: messageEvent,
    });

    if (result.stored) {
      syncedMessagesCount += 1;
    }
  }

  return reply.code(200).send({
    ok: true,
    workspace: {
      id: lawFirm.id,
      slug: lawFirm.slug,
    },
    receivedLeadEvents: leadEvents.length,
    syncedLeadIds,
    receivedMessageEvents: messageEvents.length,
    syncedMessagesCount,
  });
}

async function handleKommoSalesbotMessage(request: FastifyRequest, reply: FastifyReply) {
  const workspaceKey = getKommoWorkspaceKey({
    params: request.params,
    query: request.query,
    headers: request.headers,
  });

  if (!workspaceKey) {
    throw reply.badRequest("Informe o workspaceKey do Kommo por query ou header.");
  }

  const lawFirm = await resolveAuthorizedKommoLawFirm({
    workspaceKey,
    providedSecret: getKommoWebhookSecret({
      headers: request.headers,
      query: request.query,
    }),
  });

  if (!lawFirm) {
    throw reply.notFound("Workspace não encontrado para a integração Kommo.");
  }

  const payload = normalizePayload(request.body);
  const salesbotToken = typeof payload.token === "string" ? payload.token.trim() : "";
  const settings = await getKommoWorkspaceSettings(lawFirm.id);
  const integrationSecretKey = settings?.integrationSecretKey?.trim() ?? "";

  if (integrationSecretKey) {
    if (!salesbotToken) {
      throw reply.unauthorized("Token JWT do Salesbot não enviado pelo Kommo.");
    }

    try {
      verifyKommoWidgetRequestToken({
        token: salesbotToken,
        secretKey: integrationSecretKey,
      });
    } catch (error) {
      throw reply.unauthorized(
        error instanceof Error ? error.message : "Token JWT do Salesbot inválido.",
      );
    }
  }

  const result = await ingestKommoSalesbotMessage({
    lawFirmId: lawFirm.id,
    payload,
  });

  return reply.code(200).send({
    ok: true,
    leadRecordId: result.leadRecordId,
    conversationId: result.conversationId,
    clientId: result.clientId,
    caseId: result.caseId,
    isLinked: result.isLinked,
  });
}

export async function registerKommoIntegrationRoutes(app: FastifyInstance) {
  installKommoFormParser(app);

  app.get("/summary", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    return buildKommoIntegrationSummary({
      request,
      lawFirmId: profile.lawFirm.id,
      workspaceKey: profile.lawFirm.slug,
    });
  });

  app.post("/settings", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const payload = kommoSettingsBodySchema.parse(request.body ?? {});

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const saved = await saveKommoWorkspaceSettings({
      lawFirmId: profile.lawFirm.id,
      actorUserId: profile.user.id,
      subdomain: payload.subdomain,
      accessToken: payload.accessToken,
      webhookSecret: payload.webhookSecret,
      integrationSecretKey: payload.integrationSecretKey,
    });

    await writeAuditLog({
      lawFirmId: profile.lawFirm.id,
      officeId: profile.user.primaryOfficeId ?? null,
      actorUserId: profile.user.id,
      entityType: "law_firm_kommo_setting",
      entityId: saved?.id ?? null,
      action: "kommo.settings.save",
      afterJson: {
        subdomain: saved?.subdomain ?? null,
        accessTokenLast4: saved?.accessTokenLast4 ?? null,
        webhookSecretLast4: saved?.webhookSecretLast4 ?? null,
        integrationSecretKeyLast4: saved?.integrationSecretKeyLast4 ?? null,
      },
      request,
    });

    return reply.code(201).send(
      await buildKommoIntegrationSummary({
        request,
        lawFirmId: profile.lawFirm.id,
        workspaceKey: profile.lawFirm.slug,
      }),
    );
  });

  app.get("/leads", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const query = kommoLeadListQuerySchema.parse(request.query);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    return listKommoLeads({
      lawFirmId: profile.lawFirm.id,
      status: query.status,
      limit: query.limit,
    });
  });

  app.post("/import-leads", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const payload = kommoLeadImportBodySchema.parse(request.body ?? {});

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    const result = await importKommoLeads({
      lawFirmId: profile.lawFirm.id,
      limit: payload.limit,
    });

    await writeAuditLog({
      lawFirmId: profile.lawFirm.id,
      officeId: profile.user.primaryOfficeId ?? null,
      actorUserId: profile.user.id,
      entityType: "kommo_lead",
      entityId: null,
      action: "kommo.lead.import",
      afterJson: result,
      request,
    });

    return reply.code(201).send(result);
  });

  app.post("/leads/:leadRecordId/link-client", async (request, reply) => {
    const session = await requireSession(request, reply);
    const profile = await getSessionProfile(session);
    const { leadRecordId } = kommoLeadParamsSchema.parse(request.params);
    const payload = kommoLeadLinkBodySchema.parse(request.body);

    if (!profile) {
      throw reply.unauthorized("Session is no longer valid");
    }

    let lead;

    try {
      lead = await linkKommoLeadToClient({
        lawFirmId: profile.lawFirm.id,
        leadRecordId,
        clientId: payload.clientId,
      });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Kommo lead could not be linked";

      if (message.includes("already linked")) {
        throw reply.conflict("Este lead do Kommo já foi vinculado a outro cliente.");
      }

      if (message.includes("Client not found")) {
        throw reply.notFound("Cliente não encontrado para o vínculo do lead Kommo.");
      }

      throw error;
    }

    if (!lead) {
      throw reply.notFound("Lead do Kommo não encontrado.");
    }

    await writeAuditLog({
      lawFirmId: profile.lawFirm.id,
      officeId: profile.user.primaryOfficeId ?? null,
      actorUserId: profile.user.id,
      entityType: "kommo_lead",
      entityId: lead.id,
      action: "kommo.lead.link_client",
      afterJson: {
        leadId: lead.leadId,
        clientId: lead.linkedClientId,
        conversationId: lead.conversationId,
      },
      request,
    });

    return {
      id: lead.id,
      leadId: lead.leadId,
      displayName: lead.displayName,
      clientId: lead.linkedClientId,
      caseId: lead.linkedCaseId,
      conversationId: lead.conversationId,
      linkedAt: lead.linkedAt,
    };
  });

  app.post("/:workspaceKey/webhook", handleKommoWebhook);
  app.post("/:workspaceKey/salesbot/messages", handleKommoSalesbotMessage);
}

export async function registerKommoWebhookRoutes(app: FastifyInstance) {
  installKommoFormParser(app);

  app.post("/", handleKommoWebhook);
  app.post("/salesbot/messages", handleKommoSalesbotMessage);
}
