import {
  Anchor,
  Card,
  Group,
  Progress,
  SegmentedControl,
  Stack,
  Table,
  Text,
  Title,
  Tooltip,
} from "@mantine/core";
import { useState } from "react";
import { usePromoTheme } from "@/routes/promo-code-campaigns/_components/appearance";
import styles from "@/routes/promo-code-campaigns/_components/promo.module.css";
import { formatRevenue } from "@/lib/export/eventAnalyticsSheets";
import type { SourceFunnel as SourceFunnelRow } from "@/lib/features/event-analytics/types";

const fmtInt = (n: number) => n.toLocaleString("en-US");

/** Hours as something readable — "2.5 h" is worse than "2h 30m" at a glance. */
function formatElapsed(hours: number | null): string {
  if (hours === null) return "—";
  if (hours < 1) return `${Math.round(hours * 60)}m`;
  if (hours < 48) {
    const h = Math.floor(hours);
    const m = Math.round((hours - h) * 60);
    return m ? `${h}h ${m}m` : `${h}h`;
  }
  return `${(hours / 24).toFixed(1)} days`;
}

type Dimension = "source" | "medium";

/**
 * Where journeys from each acquisition channel stopped.
 *
 * `utm_source` is written on the add-to-cart only, so later steps inherit it
 * through the journey chain — a purchase is credited to the campaign that
 * produced its cart, not counted as untagged.
 *
 * The three outcomes partition each row: every journey that started is in
 * exactly one of them, so a stacked bar is honest here in a way it would not be
 * for the funnel stages, which contain one another.
 */
export default function SourceFunnel({
  bySource,
  byMedium,
  onOpenCampaign,
}: {
  bySource: SourceFunnelRow[];
  byMedium: SourceFunnelRow[];
  /** Opens one channel's own page. Omitted for the medium view. */
  onOpenCampaign?: (utmSource: string) => void;
}) {
  const { series } = usePromoTheme();
  const [dimension, setDimension] = useState<Dimension>("source");

  const rows = dimension === "source" ? bySource : byMedium;
  const [cartColor, holdColor, purchaseColor] = series(3);

  const totals = rows.reduce(
    (acc, r) => ({
      journeys: acc.journeys + r.journeys,
      stoppedAtCart: acc.stoppedAtCart + r.stoppedAtCart,
      stoppedAtHold: acc.stoppedAtHold + r.stoppedAtHold,
      purchased: acc.purchased + r.purchased,
    }),
    { journeys: 0, stoppedAtCart: 0, stoppedAtHold: 0, purchased: 0 },
  );

  return (
    <Card className={styles.sectionCard} p="md">
      <Stack gap="sm">
        <Group justify="space-between" align="center" wrap="wrap">
          <Stack gap={0}>
            <Title order={5}>Where journeys stopped, by channel</Title>
            <Text size="xs" c="dimmed">
              From the utm tags on the link that started the journey. Journeys
              that began in the app carry none and are grouped as Direct.
            </Text>
          </Stack>
          <SegmentedControl
            size="xs"
            value={dimension}
            onChange={(v) => setDimension(v as Dimension)}
            data={[
              { value: "source", label: "Source" },
              { value: "medium", label: "Medium" },
            ]}
          />
        </Group>

        {/*
         * Everything-Direct is a normal result, not a failure.
         *
         * Most journeys start in the app and carry no tags at all, so a table
         * holding one Direct row is correct — saying so stops it reading as
         * "the utm tracking is broken".
         */}
        {rows.length === 1 && rows[0].key === "Direct" && (
          <Text size="xs" c="dimmed">
            Every journey in this range started in the app rather than from a
            tagged link, so there is nothing to compare between channels yet.
          </Text>
        )}

        {rows.length === 0 ? (
          <Text c="dimmed" size="sm">
            No journeys in this range.
          </Text>
        ) : (
          <>
            {/* Legend once, rather than on every bar. */}
            <Group gap="lg">
              {[
                { label: "Purchased", color: purchaseColor.from, value: totals.purchased },
                { label: "Stopped at hold", color: holdColor.from, value: totals.stoppedAtHold },
                { label: "Stopped at cart", color: cartColor.from, value: totals.stoppedAtCart },
              ].map((item) => (
                <Group key={item.label} gap={6} wrap="nowrap">
                  <span
                    style={{
                      width: 10,
                      height: 10,
                      borderRadius: 2,
                      background: item.color,
                      display: "inline-block",
                    }}
                  />
                  <Text size="xs" c="dimmed">
                    {item.label} ({fmtInt(item.value)})
                  </Text>
                </Group>
              ))}
            </Group>

            <Card className={styles.tableCard} p={0}>
              <Table.ScrollContainer minWidth={720}>
                <Table highlightOnHover verticalSpacing="sm" horizontalSpacing="md">
                  <Table.Thead className={styles.tableHead}>
                    <Table.Tr>
                      <Table.Th>{dimension === "source" ? "Source" : "Medium"}</Table.Th>
                      <Table.Th ta="right">Journeys</Table.Th>
                      <Table.Th ta="right">Members</Table.Th>
                      <Table.Th w={200}>Outcome</Table.Th>
                      <Table.Th ta="right">In cart</Table.Th>
                      <Table.Th ta="right">Held</Table.Th>
                      <Table.Th ta="right">Bookings</Table.Th>
                      <Table.Th ta="right">Conversion</Table.Th>
                      <Table.Th ta="right">Time to book</Table.Th>
                      <Table.Th ta="right">Revenue</Table.Th>
                    </Table.Tr>
                  </Table.Thead>
                  <Table.Tbody>
                    {rows.map((row) => (
                      <Table.Tr key={row.key}>
                        <Table.Td>
                          {/* Direct is not a campaign, so there is no page for
                              it — only tagged channels drill in. */}
                          {dimension === "source" && row.key !== "Direct" && onOpenCampaign ? (
                            <Anchor
                              component="button"
                              type="button"
                              size="sm"
                              fw={600}
                              onClick={() => onOpenCampaign(row.key)}
                            >
                              {row.key}
                            </Anchor>
                          ) : (
                            <Text size="sm" fw={600}>
                              {row.key}
                            </Text>
                          )}
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num}>
                          {fmtInt(row.journeys)}
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num}>
                          {fmtInt(row.members)}
                        </Table.Td>
                        <Table.Td>
                          {/* Segments in funnel order, so the bar reads left to
                              right as progress and the tail is abandonment. */}
                          <Tooltip
                            withArrow
                            label={`${row.purchased} purchased · ${row.stoppedAtHold} stopped at hold · ${row.stoppedAtCart} stopped at cart`}
                          >
                            <Progress.Root size="lg" radius="sm" style={{ cursor: "help" }}>
                              <Progress.Section
                                value={(row.purchased / row.journeys) * 100}
                                color={purchaseColor.from}
                              />
                              <Progress.Section
                                value={(row.stoppedAtHold / row.journeys) * 100}
                                color={holdColor.from}
                              />
                              <Progress.Section
                                value={(row.stoppedAtCart / row.journeys) * 100}
                                color={cartColor.from}
                              />
                            </Progress.Root>
                          </Tooltip>
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num}>
                          {fmtInt(row.stoppedAtCart)}
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num}>
                          {fmtInt(row.stoppedAtHold)}
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num}>
                          {fmtInt(row.purchased)}
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num}>
                          <Text size="sm" fw={700}>
                            {row.conversionPct}%
                          </Text>
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num}>
                          <Text size="sm" c={row.medianHoursToPurchase === null ? "dimmed" : undefined}>
                            {formatElapsed(row.medianHoursToPurchase)}
                          </Text>
                        </Table.Td>
                        <Table.Td ta="right">
                          <Text size="sm">{formatRevenue(row.revenueByCurrency)}</Text>
                        </Table.Td>
                      </Table.Tr>
                    ))}
                    {rows.length > 1 && (
                      <Table.Tr>
                        <Table.Td>
                          <Text size="sm" fw={700}>
                            TOTAL
                          </Text>
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num} fw={700}>
                          {fmtInt(totals.journeys)}
                        </Table.Td>
                        <Table.Td />
                        <Table.Td />
                        <Table.Td ta="right" className={styles.num} fw={700}>
                          {fmtInt(totals.stoppedAtCart)}
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num} fw={700}>
                          {fmtInt(totals.stoppedAtHold)}
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num} fw={700}>
                          {fmtInt(totals.purchased)}
                        </Table.Td>
                        <Table.Td ta="right" className={styles.num} fw={700}>
                          {totals.journeys
                            ? `${((totals.purchased / totals.journeys) * 100).toFixed(1)}%`
                            : "—"}
                        </Table.Td>
                        {/* Medians do not add up, and revenue spans currencies. */}
                        <Table.Td />
                        <Table.Td />
                      </Table.Tr>
                    )}
                  </Table.Tbody>
                </Table>
              </Table.ScrollContainer>
            </Card>
          </>
        )}
      </Stack>
    </Card>
  );
}
