import { FC } from "react";
import styles from "./Board.module.css";
import { Divider, Text, Title } from "@mantine/core";
import { Draggable, Droppable } from "@hello-pangea/dnd";
import CreateDealButton from "../CreateDealButton/CreateDealButton.tsx";
import { DealGroupSchema, DealSummary } from "../../../client";
import DealSummaryCard from "../DealSummaryCard/DealSummaryCard.tsx";
import classNames from "classnames";
import { getPluralForm } from "../../../shared/lib/utils.ts";
import { groupBy, has, sum, uniq } from "lodash";
import { DealGroupView } from "../DealGroupView/DealGroupView.tsx";

type Props = {
    droppableId: string;
    title: string;
    withCreateButton?: boolean;
    summaries: DealSummary[];
    color: string;
};
type GroupWithDeals = { group: DealGroupSchema, deals: DealSummary[] }

export const Board: FC<Props> = ({
                                     droppableId,
                                     title,
                                     summaries,
                                     color,
                                     withCreateButton = false,
                                 }) => {
    const getDealsText = () => {
        const pluralForm = getPluralForm(
            summaries.length,
            "сделка",
            "сделки",
            "сделок",
        );
        return `${summaries.length} ${pluralForm}: ${sum(summaries.map(summary => summary.totalPrice)).toLocaleString("ru-RU")}₽`;
    };
    const isGroup = (obj: GroupWithDeals | DealSummary): obj is GroupWithDeals => {
        return has(obj, "deals");
    };


    const getDealGroups = (): GroupWithDeals[] => {
        const groups = uniq<DealGroupSchema>(summaries.filter(s => s.group).map(summary => summary.group) as DealGroupSchema[]);
        if (groups.length === 0) return [];
        const groupedSummaries = groupBy(summaries, "group.id");
        const groupDict = groups.reduce((acc, group) => {
                acc[group.id] = group;
                return acc;
            }
            , {} as { [key: number]: DealGroupSchema });
        return Object.entries(groupedSummaries).reduce((acc, [groupId, deals]) => {
            if (!groupId) return acc;
            const group = groupDict[parseInt(groupId)];
            if (!group) return acc;
            acc.push({
                group,
                deals,
            });
            return acc;
        }, [] as { group: DealGroupSchema; deals: DealSummary[] }[]);
    };

    const getDateFromDealOrGroup = (obj: GroupWithDeals | DealSummary) => {
        // if is group get group with the delivery earliest date
        if (isGroup(obj)) {
            const dates = obj.deals.map(d => d.deliveryDate || d.createdAt).filter(Boolean);
            if (dates.length === 0) return null;
            return new Date(Math.min(...dates.map(d => {
                return new Date(d).getTime();
            })));
        }
        // if is deal get deal delivery date
        return new Date(obj.deliveryDate || obj.createdAt);
    };

    const getDealsAndGroups = (): (GroupWithDeals | DealSummary)[] => {
        const groups = getDealGroups();
        const deals = summaries.filter(s => !s.group);
        const dealsWithDeliveryDate = deals.filter(d => d.deliveryDate).sort((a, b) => {
            if (!a.deliveryDate || !b.deliveryDate) return 0;
            return new Date(a.deliveryDate).getTime() - new Date(b.deliveryDate).getTime();
        });
        const dealsWithoutDeliveryDate = deals.filter(d => !d.deliveryDate).sort((a, b) => {
            return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
        });
        const groupsWithDeliveryDate = groups.filter(g => g.deals.some(d => d.deliveryDate));
        const groupsWithoutDeliveryDate = groups.filter(g => g.deals.every(d => !d.deliveryDate));
        const firstRankData = [...dealsWithDeliveryDate, ...groupsWithDeliveryDate].sort((a, b) => {
            const aDate = getDateFromDealOrGroup(a);
            const bDate = getDateFromDealOrGroup(b);
            if (!aDate || !bDate) return 0;
            return aDate.getTime() - bDate.getTime();
        });
        const secondRankData = [...dealsWithoutDeliveryDate, ...groupsWithoutDeliveryDate].sort((a, b) => {
            const aDate = getDateFromDealOrGroup(a);
            const bDate = getDateFromDealOrGroup(b);
            if (!aDate || !bDate) return 0;
            return aDate.getTime() - bDate.getTime();
        });
        return [...firstRankData, ...secondRankData].map((obj, index) => {
            if (isGroup(obj)) {
                obj.deals[0].rank = index;
                return obj;
            }
            obj.rank = index;
            return obj;
        }) as (GroupWithDeals | DealSummary)[];
    };

    const renderDeal = (deal: DealSummary) => {
        return (<Draggable

            draggableId={deal.id.toString()}
            index={deal.rank}
            key={deal.id}>
            {(provided, snapshot) => (
                <div
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                    ref={provided.innerRef}>
                    <div
                        style={{
                            transition: "transform 0.1s ease-in-out",
                            transform: snapshot.combineWith ? "scale(0.85)" : "none",
                            color: snapshot.combineWith ? "red" : "black",
                        }}
                    >
                        <DealSummaryCard
                            dealSummary={deal}
                        />
                    </div>

                </div>
            )}
        </Draggable>);
    };
    const renderGroup = (obj: GroupWithDeals) => {
        const { deals, group } = obj;
        return (<Draggable
            draggableId={"group-" + group.id}
            index={deals[0].rank}
            key={"group-" + group.id}
        >
            {(provided) => (
                <div
                    {...provided.draggableProps}
                    {...provided.dragHandleProps}
                    ref={provided.innerRef}
                >
                    <DealGroupView
                        deals={deals}
                        group={group}
                    />
                </div>
            )}
        </Draggable>);
    };
    return (
        <div className={styles["container"]}>
            <div className={styles["header"]}>
                <Title size={"h4"}>{title}</Title>
                <Text>{getDealsText()}</Text>
                <Divider
                    size={"xl"}
                    my={10}
                    color={color}
                />
            </div>
            <Droppable
                isCombineEnabled
                droppableId={droppableId}>
                {(provided, snapshot) => (
                    <div
                        ref={provided.innerRef}
                        className={classNames(
                            styles["items-list"],
                            snapshot.isDraggingOver &&
                            !snapshot.draggingFromThisWith &&
                            styles["items-list-drag-over"],
                        )}
                        {...provided.droppableProps}>
                        {withCreateButton && (
                            <CreateDealButton onClick={() => {
                            }} />
                        )}
                        {getDealsAndGroups().map(obj => {
                            if (isGroup(obj)) {
                                return renderGroup(obj);
                            }
                            return renderDeal(obj);
                        })}
                        {provided.placeholder}
                    </div>
                )}
            </Droppable>
        </div>
    );
};

export default Board;

