import { Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, Grid, IconButton } from "@mui/material";
import { Link as RouterLink, useHistory } from "react-router-dom";
import { ChevronLeft, Delete, Edit } from "@mui/icons-material";
import React, { useState } from "react";
import {
    commitTransaction,
    createTransactionChange,
    deleteTransaction,
    discardTransactionChange,
    updateTransaction,
    updateTransactionPositions,
} from "../../api";
import { toast } from "react-toastify";
import { useRecoilTransaction_UNSTABLE, useRecoilValue, useResetRecoilState, useSetRecoilState } from "recoil";
import { currUserPermissions } from "../../state/groups";
import {
    groupTransactions,
    pendingTransactionDetailChanges,
    pendingTransactionPositionChanges,
    TransactionBackend,
    updateTransactionInState,
} from "../../state/transactions";

export default function TransactionActions({ groupID, transaction }) {
    const [confirmDeleteDialogOpen, setConfirmDeleteDialogOpen] = useState(false);

    const history = useHistory();
    const userPermissions = useRecoilValue(currUserPermissions(groupID));
    const setTransactions = useSetRecoilState(groupTransactions(transaction.group_id));
    const localTransactionChanges = useRecoilValue(pendingTransactionDetailChanges(transaction.id));
    const localPositionChanges = useRecoilValue(pendingTransactionPositionChanges(transaction.id));
    const resetLocalTransactionChanges = useResetRecoilState(pendingTransactionDetailChanges(transaction.id));
    const resetLocalPositionChanges = useResetRecoilState(pendingTransactionPositionChanges(transaction.id));

    const updateTransactionAndClearLocal = useRecoilTransaction_UNSTABLE(
        ({ get, set, reset }) =>
            (transaction: TransactionBackend) => {
                set(groupTransactions(transaction.group_id), (currTransactions) => {
                    return currTransactions.map((t) => (t.id === transaction.id ? transaction : t));
                });
                reset(pendingTransactionDetailChanges(transaction.id));
                reset(pendingTransactionPositionChanges(transaction.id));
            }
    );

    const edit = () => {
        if (!transaction.is_wip) {
            createTransactionChange({
                transactionID: transaction.id,
            })
                .then((t) => {
                    updateTransactionAndClearLocal(t);
                })
                .catch((err) => {
                    toast.error(err);
                });
        }
    };

    const abortEdit = () => {
        if (transaction.is_wip) {
            if (transaction.has_committed_changes) {
                discardTransactionChange({
                    transactionID: transaction.id,
                })
                    .then((t) => {
                        updateTransactionAndClearLocal(t);
                    })
                    .catch((err) => {
                        toast.error(err);
                    });
            } else {
                history.push(`/groups/${groupID}/`);
            }
        }
    };

    const commitEdit = () => {
        if (transaction.is_wip) {
            // update the transaction given the currently pending changes
            // find out which local changes we have and send them to da server
            const positions = Object.values(localPositionChanges.modified)
                .concat(
                    Object.values(localPositionChanges.added).map((position) => ({
                        ...position,
                        id: -1,
                    }))
                )
                .map((p) => ({
                    id: p.id,
                    name: p.name,
                    communist_shares: p.communist_shares,
                    price: p.price,
                    usages: p.usages,
                    deleted: p.deleted,
                }));

            if (Object.keys(localTransactionChanges).length > 0) {
                updateTransaction({
                    transactionID: transaction.id,
                    description: transaction.description,
                    value: transaction.value,
                    billedAt: transaction.billed_at,
                    currencySymbol: transaction.currency_symbol,
                    currencyConversionRate: transaction.currency_conversion_rate,
                    creditorShares: transaction.creditor_shares,
                    debitorShares: transaction.debitor_shares,
                    ...localTransactionChanges,
                    positions: positions.length > 0 ? positions : null,
                })
                    .then((t) => {
                        updateTransactionAndClearLocal(t);
                    })
                    .catch((err) => {
                        toast.error(err);
                    });
            } else if (positions.length > 0) {
                updateTransactionPositions({
                    transactionID: transaction.id,
                    positions: positions,
                })
                    .then((t) => {
                        updateTransactionAndClearLocal(t);
                    })
                    .catch((err) => {
                        toast.error(err);
                    });
            } else {
                commitTransaction({ transactionID: transaction.id })
                    .then((t) => {
                        updateTransactionAndClearLocal(t);
                    })
                    .catch((err) => {
                        toast.error(err);
                    });
            }
        }
    };

    const confirmDeleteTransaction = () => {
        deleteTransaction({ transactionID: transaction.id })
            .then((t) => {
                // TODO: use recoil transaction
                updateTransactionInState(t, setTransactions);
                resetLocalPositionChanges();
                resetLocalTransactionChanges();
                history.push(`/groups/${groupID}/`);
            })
            .catch((err) => {
                toast.error(err);
            });
    };

    return (
        <>
            <Grid container justifyContent="space-between">
                <Grid item sx={{ display: "flex", alignItems: "center" }}>
                    <IconButton
                        sx={{ display: { xs: "none", md: "inline-flex" } }}
                        component={RouterLink}
                        to={`/groups/${groupID}/`}
                    >
                        <ChevronLeft />
                    </IconButton>
                    <Chip color="primary" label={transaction.type} />
                </Grid>
                <Grid item>
                    {userPermissions.can_write && (
                        <>
                            {transaction.is_wip ? (
                                <>
                                    <Button color="primary" onClick={commitEdit}>
                                        Save
                                    </Button>
                                    <Button color="error" onClick={abortEdit}>
                                        Cancel
                                    </Button>
                                </>
                            ) : (
                                <IconButton color="primary" onClick={edit}>
                                    <Edit />
                                </IconButton>
                            )}
                            <IconButton color="error" onClick={() => setConfirmDeleteDialogOpen(true)}>
                                <Delete />
                            </IconButton>
                        </>
                    )}
                </Grid>
            </Grid>
            <Dialog maxWidth="xs" aria-labelledby="confirmation-dialog-title" open={confirmDeleteDialogOpen}>
                <DialogTitle id="confirmation-dialog-title">Confirm delete transaction</DialogTitle>
                <DialogContent dividers>
                    Are you sure you want to delete the transaction "{transaction.description}"
                </DialogContent>
                <DialogActions>
                    <Button autoFocus onClick={() => setConfirmDeleteDialogOpen(false)} color="primary">
                        Cancel
                    </Button>
                    <Button onClick={confirmDeleteTransaction} color="error">
                        Ok
                    </Button>
                </DialogActions>
            </Dialog>
        </>
    );
}