import {
  Body,
  Button,
  Column,
  Container,
  Head,
  Heading,
  Hr,
  Html,
  Img,
  Link,
  Preview,
  Row,
  Section,
  Tailwind,
  Text,
  render,
} from "@react-email/components";
import type Mail from "nodemailer/lib/mailer";
import React from "react";
import { MailOption } from "./types";

/**
 * EDMs for the export approval flow.
 *
 * Four mails, one shell:
 *
 *  - approval needed → each super admin, with the workbook attached
 *  - approved → the requester, with the workbook attached
 *  - not approved → the requester
 *  - reviewed → each super admin, as the audit trail of who decided what, when
 *
 * They share a layout because they are read as a set: the same person often sees
 * the request notice and the review notice minutes apart, and identical structure
 * makes the difference between them the content rather than the furniture.
 *
 * Every mail carries a button. Where the recipient is a known console user it is a
 * one-time sign-in link (see lib/console-sso), so acting on the mail is one click
 * rather than a sign-in detour — the whole point of notifying someone.
 */

const LOGO_URL = "https://cdn.karmagroup.com/Karma-Group-Logo-png.png";

/*
 * Timestamps are rendered in UTC and labelled as such.
 *
 * The server's locale is not the reader's, and an unlabelled time on an audit
 * record ("who approved it, when") is worse than an inconvenient one — nobody can
 * tell whether two mails describe the same moment. Change the zone here if the
 * business would rather read local time.
 */
const DISPLAY_TIME_ZONE = "UTC";
const DISPLAY_TIME_ZONE_LABEL = "UTC";

export function formatMailTimestamp(value: Date | string | null | undefined): string {
  if (!value) return "—";
  const d = value instanceof Date ? value : new Date(value);
  if (!Number.isFinite(d.getTime())) return "—";
  const formatted = new Intl.DateTimeFormat("en-GB", {
    day: "numeric",
    month: "short",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit",
    hour12: true,
    timeZone: DISPLAY_TIME_ZONE,
  }).format(d);
  return `${formatted} ${DISPLAY_TIME_ZONE_LABEL}`;
}

export function formatFileSize(bytes: number | null | undefined): string {
  if (!bytes) return "—";
  return bytes < 1024 * 1024
    ? `${Math.max(1, Math.round(bytes / 1024))} KB`
    : `${(bytes / 1024 / 1024).toFixed(1)} MB`;
}

/**
 * The report's name, without the verb the button carries.
 *
 * `label` comes straight off the console's export button ("Export Campaigns"), so
 * using it verbatim in a sentence produces "your Export Campaigns export". The
 * console strips the same prefix for its own "Request Campaigns" button.
 */
function reportName(label: string): string {
  return label.replace(/^export\s+/i, "").trim() || label;
}

interface DetailRow {
  label: string;
  value: string | null | undefined;
}

/** Label/value rows. Rows with nothing to say are dropped rather than shown empty. */
const Details = ({ rows }: { rows: DetailRow[] }): React.ReactNode => (
  <Section className="border border-solid border-[#eaeaea] rounded px-[16px] py-[4px] my-[20px]">
    {rows
      .filter((r) => r.value !== null && r.value !== undefined && r.value !== "")
      .map((r) => (
        <Row key={r.label} className="my-[8px]">
          <Column className="align-top w-[38%]">
            <Text className="text-[#666666] text-[12px] leading-[18px] m-0 uppercase tracking-wide">
              {r.label}
            </Text>
          </Column>
          <Column className="align-top">
            <Text className="text-black text-[13px] leading-[18px] m-0 font-medium">
              {r.value}
            </Text>
          </Column>
        </Row>
      ))}
  </Section>
);

interface ShellProps {
  preview: string;
  heading: string;
  accent?: string;
  intro: React.ReactNode;
  rows: DetailRow[];
  callout?: string | null;
  cta?: { href: string; label: string } | null;
  /** Shown under the button — states that the link signs the reader in, and for whom. */
  ctaNote?: string | null;
  children?: React.ReactNode;
}

const Shell = ({
  preview,
  heading,
  accent = "#000000",
  intro,
  rows,
  callout,
  cta,
  ctaNote,
  children,
}: ShellProps): React.ReactNode => (
  <Html>
    <Head />
    <Preview>{preview}</Preview>
    <Tailwind>
      <Body className="bg-white my-auto mx-auto font-sans px-2">
        <Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[520px]">
          <Section className="mt-[24px]">
            <Img
              src={LOGO_URL}
              height="44"
              alt="Karma Group"
              className="my-0 mx-auto"
            />
          </Section>
          <Heading
            className="text-[21px] font-normal text-center p-0 my-[26px] mx-0"
            style={{ color: accent }}
          >
            {heading}
          </Heading>
          <Text className="text-black text-[14px] leading-[22px]">{intro}</Text>

          <Details rows={rows} />

          {callout && (
            <Text className="text-[#444444] text-[13px] leading-[20px] bg-[#f6f6f6] rounded px-[14px] py-[10px]">
              {callout}
            </Text>
          )}

          {children}

          {cta && (
            <Section className="text-center mt-[28px] mb-[20px]">
              <Button
                className="rounded text-white text-[13px] font-semibold no-underline text-center px-6 py-3"
                style={{ backgroundColor: accent }}
                href={cta.href}
              >
                {cta.label}
              </Button>
              {ctaNote && (
                <Text className="text-[#888888] text-[11px] leading-[16px] mt-[14px]">
                  {ctaNote}
                </Text>
              )}
            </Section>
          )}

          <Hr className="border border-solid border-[#eaeaea] my-[24px] mx-0 w-full" />
          <Text className="text-[#666666] text-[12px] leading-[20px]">
            Sent by the <span className="text-black">Karma Group</span> admin
            console. If you weren&apos;t expecting this, you can ignore it — and
            tell us if you think someone is exporting data they shouldn&apos;t be.
          </Text>
        </Container>
      </Body>
    </Tailwind>
  </Html>
);

/** Fields every one of these mails describes. */
interface ExportSummary {
  label: string;
  requestedBy: string;
  requestedAt: Date | string;
  rowCount: number;
  fileSize: number;
  filename: string;
  filtersSummary?: string | null;
}

function summaryRows(e: ExportSummary): DetailRow[] {
  return [
    { label: "Report", value: reportName(e.label) },
    { label: "Requested by", value: e.requestedBy },
    { label: "Requested at", value: formatMailTimestamp(e.requestedAt) },
    { label: "Rows", value: e.rowCount ? e.rowCount.toLocaleString() : "—" },
    { label: "File", value: `${e.filename} · ${formatFileSize(e.fileSize)}` },
    // The filters are what make an export judgeable — the same report over one
    // campaign and over everything are very different asks.
    { label: "Filters", value: e.filtersSummary || "None — full data set" },
  ];
}

async function renderMail(
  node: React.ReactNode,
  subject: string,
  mailOption: MailOption,
  attachments?: Mail.Options["attachments"],
): Promise<Mail.Options> {
  try {
    const html = await render(node);
    return {
      ...mailOption,
      subject,
      headers: { priority: "high" },
      html,
      ...(attachments?.length ? { attachments } : {}),
    };
  } catch (err) {
    console.error("[ExportMail] RENDER FAILED:", err);
    throw new Error((err as Error).message ?? "Failed to render export email");
  }
}

/* ------------------------------------------------------------------ *
 * 1. Approval needed → a super admin
 * ------------------------------------------------------------------ */

export interface ExportApprovalRequestProps extends ExportSummary {
  /** Who this copy is addressed to — the sign-in link is theirs alone. */
  reviewerEmail: string;
  reviewLink: string;
  ssoMinutes?: number;
}

/**
 * Takes no attachment, deliberately.
 *
 * The workbook is the thing under review, so putting it in this mail would deliver
 * the data to every super admin's inbox before the approval that gates it — and
 * leave copies outside anything the console can account for. Reviewers download it
 * from the queue instead.
 */
export async function sendExportApprovalRequestMail(
  props: ExportApprovalRequestProps,
  mailOption: MailOption,
): Promise<Mail.Options> {
  const report = reportName(props.label);
  const node = (
    <Shell
      preview={`${props.requestedBy} needs approval to export ${report}`}
      heading="An export needs your approval"
      accent="#b45309"
      intro={
        <>
          <strong>{props.requestedBy}</strong> generated the{" "}
          <strong>{report}</strong> export and it needs a super admin to approve
          it before it can be released.
        </>
      }
      rows={summaryRows(props)}
      callout="You can download the workbook from the queue to see exactly what this request contains. Approving releases those same bytes to the requester — nothing is re-run or re-queried in between, so what you review is what they get."
      cta={{ href: props.reviewLink, label: "Review this export" }}
      ctaNote={`This button signs you in as ${props.reviewerEmail}. It works once and expires in ${
        props.ssoMinutes ?? 60
      } minutes.`}
    />
  );

  return renderMail(
    node,
    `Approval needed: ${report} export — requested by ${props.requestedBy}`,
    mailOption,
  );
}

/* ------------------------------------------------------------------ *
 * 2. Approved → the requester
 * ------------------------------------------------------------------ */

export interface ExportApprovedProps extends ExportSummary {
  reviewedBy: string;
  reviewedAt: Date | string;
  note?: string | null;
  queueLink: string;
  ssoForEmail?: string | null;
  ssoMinutes?: number;
  /** Hours the copy in the console stays downloadable after this mail. */
  retentionHours?: number;
}

export async function sendExportApprovedMail(
  props: ExportApprovedProps,
  mailOption: MailOption,
  attachments?: Mail.Options["attachments"],
): Promise<Mail.Options> {
  const report = reportName(props.label);
  const node = (
    <Shell
      preview={`Your ${report} export was approved — the file is attached`}
      heading="Your export is ready"
      accent="#15803d"
      intro={
        <>
          Your export request was approved by <strong>{props.reviewedBy}</strong>{" "}
          on {formatMailTimestamp(props.reviewedAt)}. The workbook is attached to
          this email.
        </>
      }
      rows={[
        ...summaryRows(props),
        { label: "Approved by", value: props.reviewedBy },
        { label: "Approved at", value: formatMailTimestamp(props.reviewedAt) },
        { label: "Note", value: props.note || undefined },
      ]}
      callout={`This file contains member data. Keep it inside Karma systems, and delete your copy when you're done with it. The attachment is your lasting copy — the one in the console is removed ${
        props.retentionHours ?? 24
      } hours from now.`}
      cta={{ href: props.queueLink, label: "View your export requests" }}
      ctaNote={
        props.ssoForEmail
          ? `This button signs you in as ${props.ssoForEmail}. It works once and expires in ${
              props.ssoMinutes ?? 60
            } minutes.`
          : undefined
      }
    />
  );

  return renderMail(
    node,
    `Approved: your ${report} export`,
    mailOption,
    attachments,
  );
}

/* ------------------------------------------------------------------ *
 * 3. Not approved → the requester
 * ------------------------------------------------------------------ */

export interface ExportRejectedProps extends ExportSummary {
  reviewedBy: string;
  reviewedAt: Date | string;
  note?: string | null;
  queueLink: string;
  ssoForEmail?: string | null;
  ssoMinutes?: number;
}

export async function sendExportRejectedMail(
  props: ExportRejectedProps,
  mailOption: MailOption,
): Promise<Mail.Options> {
  const report = reportName(props.label);
  const node = (
    <Shell
      preview={`Your ${report} export was not approved`}
      heading="Your export was not approved"
      accent="#b91c1c"
      intro={
        <>
          <strong>{props.reviewedBy}</strong> reviewed your export request on{" "}
          {formatMailTimestamp(props.reviewedAt)} and did not approve it. No file
          was sent.
        </>
      }
      rows={[
        ...summaryRows(props),
        { label: "Reviewed by", value: props.reviewedBy },
        { label: "Reviewed at", value: formatMailTimestamp(props.reviewedAt) },
        // Without the reason a rejection is just a refusal; with it the requester
        // can narrow the filters and ask again rather than chase someone down.
        {
          label: "Reason",
          value: props.note || "No reason was given.",
        },
      ]}
      cta={{ href: props.queueLink, label: "View your export requests" }}
      ctaNote={
        props.ssoForEmail
          ? `This button signs you in as ${props.ssoForEmail}. It works once and expires in ${
              props.ssoMinutes ?? 60
            } minutes.`
          : undefined
      }
    />
  );

  return renderMail(node, `Not approved: your ${report} export`, mailOption);
}

/* ------------------------------------------------------------------ *
 * 4. Reviewed → every super admin (the audit copy)
 * ------------------------------------------------------------------ */

export interface ExportReviewedProps extends ExportSummary {
  decision: "approved" | "rejected";
  reviewedBy: string;
  reviewedAt: Date | string;
  note?: string | null;
  /** Whether the approved file actually reached the requester. */
  deliveryStatus: string;
  queueLink: string;
  reviewerEmail?: string | null;
  ssoMinutes?: number;
}

export async function sendExportReviewedMail(
  props: ExportReviewedProps,
  mailOption: MailOption,
): Promise<Mail.Options> {
  const approved = props.decision === "approved";
  const report = reportName(props.label);
  const node = (
    <Shell
      preview={`${report} export ${props.decision} by ${props.reviewedBy}`}
      heading={`Export ${approved ? "approved" : "rejected"}`}
      accent={approved ? "#15803d" : "#b91c1c"}
      intro={
        <>
          <strong>{props.reviewedBy}</strong>{" "}
          {approved ? "approved" : "rejected"} the <strong>{report}</strong>{" "}
          export requested by{" "}
          <strong>{props.requestedBy}</strong>. This is a record for the other
          super admins — no action is needed.
        </>
      }
      rows={[
        ...summaryRows(props),
        { label: "Decision", value: approved ? "Approved" : "Rejected" },
        { label: "Reviewed by", value: props.reviewedBy },
        { label: "Reviewed at", value: formatMailTimestamp(props.reviewedAt) },
        { label: "Note", value: props.note || undefined },
        { label: "Delivery", value: props.deliveryStatus },
      ]}
      cta={{ href: props.queueLink, label: "Open the approval queue" }}
      ctaNote={
        props.reviewerEmail
          ? `This button signs you in as ${props.reviewerEmail}. It works once and expires in ${
              props.ssoMinutes ?? 60
            } minutes.`
          : undefined
      }
    >
      <Text className="text-[#666666] text-[12px] leading-[18px]">
        Every approval is logged against the reviewer&apos;s account. See the full
        history at <Link href={props.queueLink}>Export Requests</Link>.
      </Text>
    </Shell>
  );

  return renderMail(
    node,
    `Export ${approved ? "approved" : "rejected"}: ${report} — reviewed by ${
      props.reviewedBy
    }`,
    mailOption,
  );
}
