import { Badge, Button, Checkbox, Group, Table, Text } from "@mantine/core";
import { IconCopy, IconHistory } from "@tabler/icons-react";
import type { ReactNode } from "react";
import type { PromoCode } from "@/lib/features/promo-codes/types";
import { HScrollTable } from "@/lib/components/HScrollTable";
import styles from "@/routes/promo-code-campaigns/_components/promo.module.css";

interface PromoCodesTableProps {
  promoCodes: PromoCode[];
  onEdit?: (promo: PromoCode) => void;
  onDelete?: (promo: PromoCode) => void;
  onDetach?: (promo: PromoCode) => void;
  onViewLogs?: (promo: PromoCode) => void;
  // Super-admin only: opens the create form pre-filled from this promo code.
  onDuplicate?: (promo: PromoCode) => void;
  onRowClick?: (promo: PromoCode) => void;
  canUpdate?: boolean;
  canDelete?: boolean;
  rowOffset?: number;
  signupsByCode?: Record<string, number>;
  // Header label for the signups column. Campaign details renames it to
  // "Registered"; other usages keep the default "Signups".
  signupsLabel?: string;
  // When provided, renders an extra "Logged in" column (count of members who
  // have logged in) next to the signups column, highlighted in a distinct color.
  loggedInByCode?: Record<string, number>;
  internalBookingsByCode?: Record<string, number>;
  externalBookingsByCode?: Record<string, number>;
  addedAtByCode?: Record<string, string>;
  selection?: {
    ids: Set<string>;
    onChange: (next: Set<string>) => void;
    getId: (promo: PromoCode) => string;
  };
  emptyMessage?: ReactNode;
  hideUpdatedAt?: boolean;
}

const getPromoId = (promo: PromoCode) =>
  promo.id || promo.promoCodeId || promo.promo_code_id || "";
const getPromoIsActive = (promo: PromoCode) =>
  typeof promo.isActive === "boolean" ? promo.isActive : promo.is_active;
const getPromoExpiresAt = (promo: PromoCode) =>
  promo.expiresAt || promo.expires_at || "";
const getPromoCreatedAt = (promo: PromoCode) =>
  promo.createdAt || promo.created_at || "";
const getPromoUpdatedAt = (promo: PromoCode) =>
  promo.updatedAt || promo.updated_at || "";

export const PromoCodesTable: React.FC<PromoCodesTableProps> = ({
  promoCodes,
  onEdit,
  onDelete,
  onDetach,
  onViewLogs,
  onDuplicate,
  onRowClick,
  canUpdate = true,
  canDelete = true,
  rowOffset = 0,
  signupsByCode,
  signupsLabel = "Signups",
  loggedInByCode,
  internalBookingsByCode,
  externalBookingsByCode,
  addedAtByCode,
  selection,
  emptyMessage,
  hideUpdatedAt = false,
}) => {
  const showSelection = Boolean(selection);
  const showAddedAt = Boolean(addedAtByCode);
  const showLoggedIn = Boolean(loggedInByCode);
  const showInternalBookings = Boolean(internalBookingsByCode);
  const showExternalBookings = Boolean(externalBookingsByCode);
  const hasActions = Boolean(
    onEdit || onDelete || onDetach || onViewLogs || onDuplicate,
  );
  const showUpdatedAt = !hideUpdatedAt;

  const totalCols =
    (showSelection ? 1 : 1) /* checkbox or # */ +
    6 /* name, code, signups, status, expires, created */ +
    (showLoggedIn ? 1 : 0) +
    (showInternalBookings ? 1 : 0) +
    (showExternalBookings ? 1 : 0) +
    (showUpdatedAt ? 1 : 0) +
    (showAddedAt ? 1 : 0) +
    (hasActions ? 1 : 0);

  const allIds = showSelection
    ? promoCodes.map((p) => selection!.getId(p)).filter(Boolean)
    : [];
  const allChecked =
    showSelection &&
    allIds.length > 0 &&
    allIds.every((id) => selection!.ids.has(id));
  const someChecked =
    showSelection && allIds.some((id) => selection!.ids.has(id)) && !allChecked;

  const rows =
    promoCodes.length > 0 ? (
      promoCodes.map((promo, index) => {
        const isActive = getPromoIsActive(promo);
        const expiresAt = getPromoExpiresAt(promo);
        const createdAt = getPromoCreatedAt(promo);
        const updatedAt = getPromoUpdatedAt(promo);
        const promoId = getPromoId(promo);
        const signups =
          (signupsByCode && promo.code
            ? signupsByCode[promo.code.toUpperCase()]
            : undefined) ?? 0;
        const loggedIn =
          (loggedInByCode && promo.code
            ? loggedInByCode[promo.code.toUpperCase()]
            : undefined) ?? 0;
        const internalBookings =
          (internalBookingsByCode && promo.code
            ? internalBookingsByCode[promo.code.toUpperCase()]
            : undefined) ?? 0;
        const externalBookings =
          (externalBookingsByCode && promo.code
            ? externalBookingsByCode[promo.code.toUpperCase()]
            : undefined) ?? 0;
        const addedAt =
          addedAtByCode && promo.code
            ? addedAtByCode[promo.code.toUpperCase()]
            : undefined;
        const selectionId = showSelection ? selection!.getId(promo) : "";
        const isSelected = showSelection && selection!.ids.has(selectionId);

        return (
          <Table.Tr
            key={promoId || promo.code || String(index)}
            className={styles.row}
            /*
             * `.row` assumes a clickable row, so non-clickable tables have to
             * put the default cursor back — otherwise every row would advertise
             * a click that does nothing.
             */
            style={onRowClick ? undefined : { cursor: "default" }}
            onClick={onRowClick ? () => onRowClick(promo) : undefined}
          >
            {showSelection ? (
              <Table.Td
                style={{ width: 36 }}
                onClick={(e) => e.stopPropagation()}
              >
                <Checkbox
                  size="xs"
                  aria-label={`Select ${promo.code || promo.name}`}
                  checked={isSelected}
                  onChange={(e) => {
                    const checked = Boolean(e?.target?.checked);
                    const next = new Set(selection!.ids);
                    if (checked) next.add(selectionId);
                    else next.delete(selectionId);
                    selection!.onChange(next);
                  }}
                />
              </Table.Td>
            ) : (
              <Table.Td>{rowOffset + index + 1}</Table.Td>
            )}
            <Table.Td>{promo.name || "-"}</Table.Td>
            <Table.Td>
              <Text fw={600}>{promo.code || "-"}</Text>
            </Table.Td>
            <Table.Td className={styles.num}>
              <Text
                fw={signups > 0 ? 600 : 400}
                c={signups > 0 ? undefined : "dimmed"}
              >
                {signups}
              </Text>
            </Table.Td>
            {showLoggedIn && (
              <Table.Td className={styles.num}>
                <Text fw={loggedIn > 0 ? 700 : 400} c="teal">
                  {loggedIn}
                </Text>
              </Table.Td>
            )}
            {/*
             * Booking counts are badged with the same --role-internal /
             * --role-curated colours the donut slices use, so a number in the
             * table and its slice in the chart are recognisably the same series.
             * The CSS falls back to blue/violet off-theme, which is what these
             * cells used before.
             */}
            {showInternalBookings && (
              <Table.Td className={styles.num}>
                <span className={styles.dataBadge} data-role="internal">
                  {internalBookings}
                </span>
              </Table.Td>
            )}
            {showExternalBookings && (
              <Table.Td className={styles.num}>
                <span className={styles.dataBadge} data-role="curated">
                  {externalBookings}
                </span>
              </Table.Td>
            )}
            <Table.Td>
              {typeof isActive === "boolean" ? (
                <Badge color={isActive ? "green" : "gray"} variant="light">
                  {isActive ? "Active" : "Inactive"}
                </Badge>
              ) : (
                <Text c="dimmed" size="sm">
                  —
                </Text>
              )}
            </Table.Td>
            <Table.Td>
              {expiresAt ? new Date(expiresAt).toLocaleDateString() : "-"}
            </Table.Td>
            <Table.Td>
              {createdAt ? new Date(createdAt).toLocaleString() : "-"}
            </Table.Td>
            {showAddedAt && (
              <Table.Td>
                {addedAt ? new Date(addedAt).toLocaleDateString() : "—"}
              </Table.Td>
            )}
            {showUpdatedAt && (
              <Table.Td>
                {updatedAt ? new Date(updatedAt).toLocaleString() : "-"}
              </Table.Td>
            )}
            {hasActions && (
              <Table.Td onClick={(e) => e.stopPropagation()}>
                <Group gap="xs">
                  {onEdit && (
                    <Button
                      size="xs"
                      variant="light"
                      onClick={() => onEdit(promo)}
                      title={
                        !canUpdate
                          ? "You do not have update permission."
                          : isActive
                            ? "Active promo codes cannot be edited."
                            : undefined
                      }
                    >
                      Edit
                    </Button>
                  )}
                  {onDelete && (
                    <Button
                      size="xs"
                      variant="light"
                      color="red"
                      onClick={() => onDelete(promo)}
                      disabled={!canDelete || isActive}
                      title={
                        !canDelete
                          ? "You do not have delete permission."
                          : isActive
                            ? "Active promo codes cannot be deleted."
                            : undefined
                      }
                    >
                      Delete
                    </Button>
                  )}
                  {onDuplicate && (
                    <Button
                      size="xs"
                      variant="light"
                      color="grape"
                      px="xs"
                      onClick={() => onDuplicate(promo)}
                      title="Copy this promo code's data into a new promo code"
                      aria-label="Duplicate promo code"
                    >
                      <IconCopy size={14} />
                    </Button>
                  )}
                  {onViewLogs && (
                    <Button
                      size="xs"
                      variant="subtle"
                      color="gray"
                      leftSection={<IconHistory size={12} />}
                      onClick={() => onViewLogs(promo)}
                      title="View activity logs"
                    >
                      Logs
                    </Button>
                  )}
                  {onDetach && (
                    <Button
                      size="xs"
                      variant="light"
                      color="red"
                      onClick={() => onDetach(promo)}
                      title="Remove this promo code's attachment to the campaign"
                    >
                      Detach
                    </Button>
                  )}
                </Group>
              </Table.Td>
            )}
          </Table.Tr>
        );
      })
    ) : (
      <Table.Tr>
        <Table.Td colSpan={totalCols} style={{ textAlign: "center" }}>
          {emptyMessage ?? "No promo codes found"}
        </Table.Td>
      </Table.Tr>
    );

  return (
    <HScrollTable minWidth={1100}>
      {/*
       * Hover and cell borders come from promo.module.css rather than Mantine's
       * `withTableBorder`/`highlightOnHover`/`striped`: the promo row hover is
       * accent-tinted with a leading rail, which striping would fight.
       *
       * No outer frame here on purpose — every caller renders this inside a
       * bordered Card, so adding `.tableCard` would draw a second border just
       * inside the first.
       */}
      <Table stickyHeader style={{ minWidth: 1100 }}>
        <Table.Thead className={styles.tableHead}>
          <Table.Tr>
            {showSelection ? (
              <Table.Th className={styles.th} style={{ width: 36 }}>
                <Checkbox
                  size="xs"
                  aria-label="Select all"
                  checked={allChecked}
                  indeterminate={someChecked}
                  onChange={(e) => {
                    const checked = Boolean(e?.target?.checked);
                    selection!.onChange(checked ? new Set(allIds) : new Set());
                  }}
                />
              </Table.Th>
            ) : (
              <Table.Th className={styles.th}>#</Table.Th>
            )}
            <Table.Th className={styles.th}>Name</Table.Th>
            <Table.Th className={styles.th}>Code</Table.Th>
            <Table.Th className={styles.th}>{signupsLabel}</Table.Th>
            {showLoggedIn && (
              <Table.Th className={styles.th}>Logged in</Table.Th>
            )}
            {showInternalBookings && (
              <Table.Th className={styles.th}>Karma Subito Bookings</Table.Th>
            )}
            {showExternalBookings && (
              <Table.Th className={styles.th}>Curated Bookings</Table.Th>
            )}
            <Table.Th className={styles.th}>Status</Table.Th>
            <Table.Th className={styles.th}>Expires</Table.Th>
            <Table.Th className={styles.th}>Created At</Table.Th>
            {showAddedAt && (
              <Table.Th className={styles.th}>Added to Campaign</Table.Th>
            )}
            {showUpdatedAt && (
              <Table.Th className={styles.th}>Updated At</Table.Th>
            )}
            {hasActions && <Table.Th className={styles.th}>Actions</Table.Th>}
          </Table.Tr>
        </Table.Thead>
        <Table.Tbody>{rows}</Table.Tbody>
      </Table>
    </HScrollTable>
  );
};
