import React, { useEffect, useState } from "react";

import ListAltOutlinedIcon from "@mui/icons-material/ListAltOutlined";
import Checkbox from "@mui/material/Checkbox";
import IconButton from "@mui/material/IconButton";
import Input from "@mui/material/Input";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableRow from "@mui/material/TableRow";
import Typography from "@mui/material/Typography";

import { TableCell } from "../../../components/Table/TableCell";
import TableHead from "../../../components/Table/TableHead";
import TableHeading from "../../../components/Table/TableHeading";
import { useDialog } from "../../../contexts/DialogContext";
import { useI18n } from "../../../contexts/I18nContext";
import { Adjustment, ItemProperties, Line } from "../types/purchase";

export const HIDDEN_ITEM_TYPES = ["SHIPPING", "PICKUP"];

export interface ReceiptTableProps {
  currency: string;
  lines: Line[];
  editable?: boolean;
  onSelect?: (selected: Line[]) => void;
}

export const ReceiptTable: React.FC<ReceiptTableProps> = ({
  lines,
  currency,
  editable,
  onSelect,
}) => {
  const [selected, setSelected] = useState<Line[]>([]);
  const { t } = useI18n();
  const openDialog = useDialog();

  const refundableLines = lines.filter((line) =>
    Boolean(line.adjustments.reduce((t, v) => t + v.quantity, line.quantity)),
  );

  const sortedLines = lines.sort((a, b) => a.line_number - b.line_number);
  // Group lines by their whole number part, sorted by their decimal part
  const groupedLines = sortedLines.reduce<Line[][]>((lines, line) => {
    const lineNumber = Math.floor(line.line_number);
    lines[lineNumber] ??= [];
    lines[lineNumber].push(line);
    return lines;
  }, []);

  const toggleSelectAll = (_: React.ChangeEvent<HTMLInputElement>, checked: boolean) => {
    setSelected(checked ? refundableLines : []);
  };

  const toggleLine = (item: Line, checked: boolean) => {
    const index = selected.map(({ line_number }) => line_number).indexOf(item.line_number);

    if (checked && index === -1) {
      setSelected([...selected, item]);
    } else {
      const unselected = [...selected];
      unselected.splice(index, 1);
      setSelected(unselected);
    }
  };

  const lineQuantityChange = (item: Line) => {
    const index = selected.map(({ line_number }) => line_number).indexOf(item.line_number);
    const changed = [...selected];
    changed[index] = item;
    setSelected(changed);
  };

  useEffect(() => {
    if (onSelect != null) onSelect(selected);
  }, [onSelect, selected]);

  const showItemProperties = async (title: string, itemProperties: ItemProperties) => {
    await openDialog(
      title,
      <Table size="small" sx={{ "& td": { border: 0 } }}>
        {Object.entries(itemProperties ?? {}).map(([key, value]) => (
          <TableRow key={key}>
            <TableCell variant="head">{key.toUpperCase()}</TableCell>
            <TableCell variant="body">{`${value}`}</TableCell>
          </TableRow>
        ))}
      </Table>,
      {
        ok: false,
        cancel: "Close",
      },
    );
  };

  return (
    <Table>
      <TableHead>
        {editable && (
          <TableHeading align="left" padding="checkbox">
            <Checkbox
              checked={lines.length !== 0 && selected.length === refundableLines.length}
              color="secondary"
              disabled={lines.length === 0}
              indeterminate={selected.length > 0 && selected.length < refundableLines.length}
              onChange={toggleSelectAll}
            />
          </TableHeading>
        )}
        <TableHeading align="left">{t("Item")}</TableHeading>
        <TableHeading align="left">{t("Reference")}</TableHeading>
        {["Quantity", "Unit Price", "Total", "Debit"].map((v, i) => (
          <TableHeading key={i} align="right">
            {v}
          </TableHeading>
        ))}
      </TableHead>

      <TableBody>
        {groupedLines.flatMap((lines) =>
          lines.flatMap((line, index) => {
            const isSelected = selected
              .map(({ line_number }) => line_number)
              .includes(line.line_number);

            const currentQuantity = line.adjustments.reduce(
              (t, v) => t + v.quantity,
              line.quantity,
            );

            const firstLine = index === 0;
            const subLine = !firstLine;
            const finalLine = index === lines.length - 1;

            const borderBottom = {
              borderBottomStyle: !finalLine ? "dotted" : undefined,
              borderBottomWidth: !finalLine ? 2 : undefined,
            } as const;

            const disabled = currentQuantity === 0;

            const firstRow = (
              <TableRow
                key={line.line_number}
                sx={{
                  "& td": {
                    ...borderBottom,
                    borderBottom: editable || line.adjustments.length ? "none" : undefined,
                    paddingY: 0,
                    paddingTop: 1,
                    paddingBottom: line.adjustments.length ? undefined : 1,
                    whiteSpace: "nowrap",
                  },
                }}
              >
                {editable ? (
                  <TableCell align="left" padding="checkbox" sx={{ verticalAlign: "top" }}>
                    {disabled ? null : (
                      <Checkbox
                        checked={isSelected && currentQuantity !== 0}
                        color="secondary"
                        onChange={(_, checked) =>
                          toggleLine({ ...line, quantity: currentQuantity }, checked)
                        }
                      />
                    )}
                  </TableCell>
                ) : null}

                <TableCell align="left">
                  <Typography fontSize="0.875rem" paddingLeft={subLine ? 2 : undefined}>
                    {line.title}
                    {line.item_properties && (
                      <IconButton
                        aria-label="Show line item properties"
                        size="small"
                        onClick={() =>
                          showItemProperties(line.title, line.item_properties as ItemProperties)
                        }
                      >
                        <ListAltOutlinedIcon fontSize="small" />
                      </IconButton>
                    )}
                  </Typography>
                </TableCell>
                <TableCell align="left">
                  {["SHIPPING", "PICKUP"].includes(line.item_type) ? null : line.reference}
                </TableCell>
                <TableCell align="right" typographyProps={{ fontSize: "0.875rem" }}>
                  {line.quantity}
                </TableCell>
                <TableCell align="right">
                  {line.unit_price} {currency}
                </TableCell>
                <TableCell align="right">
                  <Typography fontSize="0.875rem">
                    {line.total_amount} {currency}
                  </Typography>
                </TableCell>
                <TableCell align="right">
                  <Typography fontSize="0.875rem">
                    {line.total_amount} {currency}
                  </Typography>
                </TableCell>
              </TableRow>
            );

            const adjustmentsRows = line.adjustments.map((adjustment, i) => {
              const lastRow = !editable && i == line.adjustments.length - 1;

              const typeNameMap: Record<Adjustment["title"], string> = {
                CANCELLATION: "Cancellation",
                RETURN: "Return",
                REFUND: "Refund",
                DISCOUNT: "Discount",
              };

              const withTitle = (name: string, title: string) => {
                if (title) {
                  return `${typeNameMap[name]}: ${title}`;
                } else {
                  return typeNameMap[name];
                }
              };

              const pending = adjustment.receipt_number === null;

              return (
                <TableRow
                  key={line.line_number + String(i)}
                  sx={{
                    "& td": {
                      ...borderBottom,
                      borderBottom: lastRow ? undefined : "none",
                      color: "grey.600",
                      fontStyle: pending ? "italic" : undefined,
                      py: 0,
                      pb: lastRow ? 1 : undefined,
                      whiteSpace: "nowrap",
                    },
                  }}
                >
                  {editable && (
                    <TableCell align="left" padding="checkbox" sx={{ paddingTop: 0.7 }} />
                  )}
                  <TableCell typographyProps={{ pl: subLine ? 4 : 2, fontSize: "0.875rem" }}>
                    {withTitle(adjustment.adjustment_type, adjustment.title)}
                  </TableCell>
                  <TableCell />
                  <TableCell align="right">{adjustment.quantity}</TableCell>
                  <TableCell />
                  <TableCell align="right" style={{ verticalAlign: "top" }}>
                    {adjustment.total_amount} {currency}
                  </TableCell>
                  <TableCell align="right" typographyProps={{ fontSize: "0.875rem" }}>
                    {Boolean(lastRow && line.remaining_amount != line.total_amount) &&
                      `${line?.remaining_amount} ${currency}`}
                  </TableCell>
                </TableRow>
              );
            });

            const rows = [firstRow, ...adjustmentsRows];

            if (editable) {
              rows.push(
                <TableRow sx={{ "& td": { pt: 0, pb: 1 } }}>
                  <TableCell />
                  <TableCell />
                  <TableCell />
                  <TableCell align="right">
                    {!disabled && (
                      <Input
                        defaultValue={currentQuantity * -1}
                        disabled={!isSelected}
                        inputProps={{
                          max: Math.max(currentQuantity * -1, -1),
                          min: currentQuantity * -1,
                        }}
                        type="number"
                        onChange={(event) =>
                          lineQuantityChange({
                            ...line,
                            quantity: Number.parseInt(event.target.value) * -1,
                          })
                        }
                      />
                    )}
                  </TableCell>
                  <TableCell />
                  <TableCell />
                  <TableCell />
                </TableRow>,
              );
            }

            return rows;
          }),
        )}
      </TableBody>
    </Table>
  );
};
