import React, { useEffect, useRef } from "react";
import {
  SmartScheduleReportPage,
  SmartScheduleStats,
  useToast,
} from "@komi-app/creator-ui";

import {
  CampaignScheduleType,
  CampaignStatus,
  EMAIL_INITIAL_SUBSET_SIZE,
  EmailStatsAggCampaign,
} from "@komi-app/messaging-sdk";

import { useMutation, useQuery } from "@tanstack/react-query";
import { useQueryParameters } from "hooks/useQueryParameters";

import { useSmartScheduleCampaign } from "hooks/useSmartScheduleCampaign";
import { useEmailCampaignTemplate } from "hooks/campaigns/email/useEmailCampaignTemplate";
import { useEmailCampaignStats } from "hooks/campaigns/email/useEmailCampaignStats";
import { useEmailCampaignClickStatsQuery } from "hooks/useCampaignLinkStatsQuery";
import { useSegmentsTotalRecipients } from "hooks/useEmailCampaignTotalRecipients";

import { getSegmentServiceV1 } from "services";

import { calculateEstimatedCompletionDate } from "../../Utils";
import { campaignsUrl } from "constants/dca";
import { jsPDF } from "jspdf";
import { addFonts } from "../../fonts";

import "./EmailCampaignReport.scss";

export const payloadToForm = (
  // TODO: ESS_NORMALIZE: replace `SmartScheduleStats` with correctly exported platform type
  //    then kill this method
  stats: Omit<EmailStatsAggCampaign, "campaignId">,
  recipientCount: number,
): SmartScheduleStats => {
  if (recipientCount === 0) {
    return {
      campaignProgressPercent: 0,
      emailsSent: 0,
      openPercent: 0,
      clickThroughPercent: 0,
      bouncedPercent: 0,
      unsubscribedPercent: 0,
    };
  }

  const sent = stats.processed;

  return {
    campaignProgressPercent: sent / recipientCount,
    emailsSent: sent,
    openPercent: stats.uniqueOpened / stats.processed,
    clickThroughPercent: stats.uniqueClicked / stats.processed,
    bouncedPercent: stats.uniqueBounced / stats.processed,
    unsubscribedPercent: stats.unsubscribed / stats.processed,
  };
};

export function EmailCampaignReport() {
  const { campaignId, previousPageUrl } = useQueryParameters<{
    campaignId: string;
    previousPageUrl: string;
  }>();

  const { createToast } = useToast();

  const campaignRequest = useSmartScheduleCampaign(campaignId);
  const campaign = campaignRequest.data;

  useEffect(() => {
    if (campaignRequest.isError) {
      createToast({
        text: "We couldn't retrieve the campaign details. Try again later.",
        semantic: "warning",
        durationMs: 5000,
        dismissible: true,
      });
    }
  }, [campaignRequest.isError, createToast]);

  const campaignTemplateRequest = useEmailCampaignTemplate(
    campaign?.campaignTemplateId,
  );
  const campaignTemplate = campaignTemplateRequest?.data;

  const emailStatsQuery = useEmailCampaignStats(campaignId);
  const emailStats = emailStatsQuery?.data?.stats;

  const linkStatsQuery = useEmailCampaignClickStatsQuery(campaignId);
  const linkStats = linkStatsQuery?.data?.clicks || [];

  const emailClickStats = linkStats.map(
    ({ url, uniqueClicks, totalClicks }) => ({
      name: url,
      unique: uniqueClicks,
      total: totalClicks,
    }),
  );

  const ref = useRef<HTMLDivElement | null>(null);
  const pdfGenerationData = ref.current?.firstChild
    ? {
        ready: true as const,
        element: ref.current.firstChild as HTMLElement,
        campaignName: campaign?.name || "Email Campaign",
      }
    : { ready: false as const };

  const pdfCreationMutation = useMutation(async () => {
    if (pdfGenerationData.ready) {
      await generatePDF(
        pdfGenerationData.element,
        pdfGenerationData.campaignName,
      );

      createToast({
        text: `"${pdfGenerationData.campaignName}" Report exported to PDF`,
        semantic: "success",
        dismissible: true,
        durationMs: 5000,
      });
    }
  });

  const segments = useQuery({
    queryKey: ["segments", campaignId],
    queryFn: async () => {
      if (!campaign?.segmentIds) return [];
      return getSegmentServiceV1().getManyByIds(campaign.segmentIds);
    },
    enabled: Boolean(campaign?.segmentIds),
  });
  const segmentNames = segments.data
    ? segments.data.map((segment) => segment.name)
    : [];

  useEffect(() => {
    if (segments.isError) {
      createToast({
        text: "We couldn't retrieve the campaign segments. Try again later.",
        semantic: "warning",
        durationMs: 5000,
        dismissible: true,
      });
    }
  }, [segments.isError, createToast]);

  const totalRecipients = useSegmentsTotalRecipients(
    campaign?.segmentIds,
    campaign?.scheduledTimestamp || undefined,
  );

  const recipients =
    campaign?.status === CampaignStatus.COMPLETED
      ? emailStats?.processed
      : totalRecipients.data;

  const pdfLoading =
    pdfCreationMutation.isLoading ||
    campaignRequest.isLoading ||
    segments.isLoading ||
    linkStatsQuery.isLoading;

  const allAtOnce = campaign?.scheduleType === CampaignScheduleType.ALL_AT_ONCE;
  const stats = emailStats
    ? payloadToForm(emailStats, recipients || 0)
    : undefined;

  return (
    <div ref={ref}>
      <SmartScheduleReportPage
        previousPageUrl={
          validatePreviousPageUrl(previousPageUrl) || campaignsUrl
        }
        onExportAsPdf={pdfCreationMutation.mutate}
        pdfLoading={pdfLoading}
        onPauseCampaign={() => {}}
        onResumeCampaign={() => {}}
        onStopCampaign={() => {}}
        campaignLoading={campaignRequest.isLoading || campaignRequest.isError}
        campaign={{
          title: campaign?.name || "",
          subject: campaign?.subject || "",
          template: campaignTemplate?.name || "",
        }}
        segmentsLoading={segments.isLoading || segments.isError}
        recipientsLoading={totalRecipients.isLoading || totalRecipients.isError}
        smartSchedule={{
          status: campaign?.status || CampaignStatus.DRAFT,
          recipients: recipients || 0,
          segments: segmentNames,
          startDate: campaign?.scheduledTimestamp
            ? new Date(campaign.scheduledTimestamp)
            : new Date(),
          endDate:
            calculateEstimatedCompletionDate({
              startDate: campaign?.scheduledTimestamp
                ? new Date(campaign.scheduledTimestamp)
                : new Date(),
              totalRecipients: recipients || 0,
              initialDailySends:
                campaign?.initialSize || EMAIL_INITIAL_SUBSET_SIZE,
              scheduleType:
                campaign?.scheduleType || CampaignScheduleType.SMART_SCHEDULE,
            }) || new Date(),
        }}
        stats={stats}
        isClickStatsLoading={linkStatsQuery.isLoading || linkStatsQuery.isError}
        clicks={emailClickStats}
        campaignPreviewElement={
          campaignTemplate?.thumbnailUrl ? (
            <img
              className="template-preview-image"
              src={`${campaignTemplate.thumbnailUrl}?crossorigin`}
              alt="Email Template Preview"
              crossOrigin="anonymous"
            />
          ) : null
        }
        allAtOnce={allAtOnce}
      />
    </div>
  );
}

/**
 * This ensures that the previous page URL is a valid admin URL & does not contain any malicious content.
 * @param url
 */
function validatePreviousPageUrl(url: string | null): string | null {
  if (!url) {
    return null;
  }

  return url.startsWith("/admin") ? url : null;
}

/**
 * Ran just before the PDF is generated
 * @param doc - A clone of the current page, used to render the PDF.
 */
function preprocessPage(doc: Document) {
  const segmentLength = 28;

  const tags = doc.querySelectorAll('ul [data-testid="komiTag"] > span');
  for (const tag of tags) {
    if (!tag.textContent) continue;
    if (tag.textContent.length <= segmentLength) continue;

    tag.textContent =
      tag.textContent.substring(0, segmentLength - 3).trim() + "...";
  }
}

async function generatePDF(element: HTMLElement, campaignName: string) {
  const isoDateString = new Date().toISOString();
  const width = 1400;
  const margin = 16;

  const elementRect = element.getBoundingClientRect();

  const doc = new jsPDF({
    orientation: "landscape",
    unit: "px",
    format: [elementRect.height + margin * 4, width + margin * 2],
  });

  await addFonts(doc);

  doc.setFillColor("#f6f6f7");
  doc.rect(
    0,
    0,
    doc.internal.pageSize.getWidth(),
    doc.internal.pageSize.getHeight(),
    "F",
  );

  await new Promise<void>((resolve) => {
    doc.html(element, {
      margin,
      width,
      windowWidth: width,
      html2canvas: {
        allowTaint: true,
        useCORS: true,
        backgroundColor: "#f6f6f7",
        letterRendering: true,
        ignoreElements: (el) =>
          el instanceof SVGElement ||
          el instanceof HTMLButtonElement ||
          el.role === "menu",
        onclone: preprocessPage,
      },
      callback: () => {
        doc.save(`${campaignName}-${isoDateString}.pdf`);
        resolve();
      },
    });
  });
}
