import React, { useCallback, useMemo, useState } from "react";
import { useParams } from "react-router-dom";

import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Card from "@mui/material/Card";
import CardActions from "@mui/material/CardActions";
import Stack from "@mui/material/Stack";
import { useTheme } from "@mui/material/styles";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableContainer from "@mui/material/TableContainer";
import TableRow from "@mui/material/TableRow";

import { TableCell } from "../../../components/Table/TableCell";
import TableHead from "../../../components/Table/TableHead";
import TableHeading from "../../../components/Table/TableHeading";
import TableCardHeader from "../../../components/TableCardHeader";
import { useApi } from "../../../contexts/ApiContext";
import { useDialog } from "../../../contexts/DialogContext";
import { useI18n } from "../../../contexts/I18nContext";
import { useUser } from "../../../contexts/UserContext";
import { hasPermission } from "../../../util/has_permission";
import { Price } from "../types/contrib";

import { PriceRow } from "./PriceRow";

export interface PricesCardProps {
  prices: Price[];
  setPrices: (prices: Price[]) => void;
}

export const PricesCard: React.FC<PricesCardProps> = ({ prices, setPrices }) => {
  const theme = useTheme();
  const openDialog = useDialog();
  const api = useApi();
  const params = useParams();
  const { t } = useI18n();
  const { user } = useUser();

  const [isEditing, setIsEditing] = useState(false);
  const [isDisabled, setIsDisabled] = useState(false);

  const [changedPrices, setChangedPrices] = useState<Record<string, string>>({});
  const [deletedPrices, setDeletedPrices] = useState<string[]>([]);
  const [createdPrices, setCreatedPrices] = useState<Price[]>([]);

  const hasChanges = useMemo(
    () =>
      Object.entries(changedPrices).length > 0 ||
      deletedPrices.length > 0 ||
      createdPrices.length > 0,
    [changedPrices, deletedPrices, createdPrices],
  );

  const shownPrices = useMemo(() => {
    if (!isEditing) return prices;

    return prices
      .map(({ amount, ...price }) => {
        if (deletedPrices.includes(price.article_code)) {
          return null;
        }

        return {
          ...price,
          amount: changedPrices[price.article_code] ?? amount,
        };
      })
      .filter((price): price is Price => price !== null);
  }, [prices, isEditing, changedPrices, deletedPrices]);

  const clear = useCallback(() => {
    setChangedPrices({});
    setDeletedPrices([]);
    setCreatedPrices([]);
    setIsEditing(false);
  }, []);

  const save = useCallback(async () => {
    const action = api.operations["pricing.contrib:prices-update"];
    if (!action) {
      throw new Error('Invalid action "pricing.contrib:prices-update".');
    }

    setIsDisabled(true);
    const response = await action.call({
      params,
      body: {
        updates: [
          ...Object.entries(changedPrices).map(([article_code, amount]) => ({
            article_code,
            amount,
          })),
          ...createdPrices,
        ],
        deleted: deletedPrices,
      },
    });
    setIsDisabled(false);

    if (response.ok) {
      const updatedPrices = await response.json();
      setPrices(updatedPrices);
      clear();
    } else {
      console.error("[PRICES_CARD]", response);
    }
  }, [changedPrices, deletedPrices, createdPrices]);

  return (
    <Card
      sx={{
        boxShadow: 0,
        borderRadius: 3,
        borderWidth: "1px",
        borderStyle: "solid",
        borderColor: theme.palette.divider,
        backgroundColor: theme.palette.background.paper,
        overflow: "visible",
        width: "100%",
      }}
    >
      <TableCardHeader
        isDisabled={isDisabled}
        isEditable={hasPermission(user, "pricing.change_price")}
        title="Prices"
        toggled={isEditing}
        onChange={async (isEditing) => {
          if (hasChanges) {
            if (
              await openDialog(
                "Unsaved changes",
                "You have unsaved changes. Are you sure you want to discard your changes?",
                { ok: "Discard changes", cancel: "Cancel" },
              )
            ) {
              clear();
            }
          } else {
            setIsEditing(isEditing);
          }
        }}
      />

      <TableContainer>
        <Table>
          <TableHead>
            <TableHeading>{t("Article number")}</TableHeading>
            <TableHeading align="right">{t("Amount")}</TableHeading>
            <TableHeading />
          </TableHead>

          <TableBody
            sx={{
              ".MuiTableRow-root:last-child > .MuiTableCell-root": { borderBottom: "none" },
            }}
          >
            {shownPrices.length === 0 && (
              <TableRow>
                <TableCell align="center" colSpan={3} sx={{ color: "GrayText" }}>
                  {t("No prices")}
                </TableCell>
              </TableRow>
            )}

            {shownPrices.map((price, i) => (
              <PriceRow
                key={i}
                isDisabled={isDisabled}
                isEditing={isEditing}
                price={price}
                onChangeAmount={(amount) => {
                  setChangedPrices((prev) => ({ ...prev, [price.article_code]: amount }));
                }}
                onDelete={() => {
                  setDeletedPrices((prev) => [...prev, price.article_code]);
                }}
              />
            ))}

            {isEditing && (
              <>
                {createdPrices.map((price, i) => (
                  <PriceRow
                    key={i}
                    isDisabled={isDisabled}
                    isEditing={true}
                    price={price}
                    onChangeAmount={(amount) => {
                      setCreatedPrices((prev) =>
                        prev.map((p, j) => (i === j ? { ...p, amount } : p)),
                      );
                    }}
                    onChangeArticleCode={(article_code) => {
                      setCreatedPrices((prev) =>
                        prev.map((p, j) => (i === j ? { ...p, article_code } : p)),
                      );
                    }}
                    onDelete={() => {
                      setCreatedPrices((prev) => prev.filter((_, j) => j !== i));
                    }}
                  />
                ))}
              </>
            )}
          </TableBody>
        </Table>
      </TableContainer>

      {isEditing && (
        <Stack direction="row" justifyContent="space-between">
          <Box p={[1, 2, 3, 2]}>
            <Button
              disabled={isDisabled}
              size="medium"
              variant="outlined"
              onClick={() => {
                setCreatedPrices((prev) => [
                  ...prev,
                  { article_code: "", amount: "", currency: shownPrices[0]?.currency ?? "" },
                ]);
              }}
            >
              Add price
            </Button>
          </Box>

          <CardActions
            sx={{
              p: [1, 2, 3, 2],
              display: "flex",
              justifyContent: "flex-end",
            }}
          >
            <Button disabled={isDisabled} size="medium" variant="outlined" onClick={clear}>
              {t("Cancel")}
            </Button>
            <Button disabled={isDisabled} size="medium" variant="contained" onClick={save}>
              {t("Save")}
            </Button>
          </CardActions>
        </Stack>
      )}
    </Card>
  );
};
