import React from "react";
import * as dFns from "date-fns";
import * as R from 'ramda';

interface DateValue {
    date: Date;
    count: number;
}

interface Props {
    startDate: Date;
    endDate: Date;
    data: DateValue[];
    className?: string;
    goal: number;
    penaliseOvertime?: boolean;
}

function GitHubCalendarHeatmap({
                                   startDate,
                                   endDate,
                                   data,
                                   className,
                                   goal,
                                   penaliseOvertime = false,
                               }: Props) {
    const numDays = dFns.differenceInDays(endDate, startDate);

    // Calculate the number of weeks in the range
    const numWeeks = Math.ceil(numDays / 7);

    // Calculate the size of each square in the heatmap and the padding between each square
    const squareSize = 15;
    const padding = 2;
    const weekTotalPadding = 10; // Extra padding for the week total row
    const totalWidth = numWeeks * (squareSize + padding);
    const totalHeight = 8 * (squareSize + padding) + weekTotalPadding; // Added an extra row for week total

    // Goal times for the daily and weekly squares
    const dayGoalTime = goal;
    const weekGoalTime = 5 * dayGoalTime;

    const getColorForValue = (value: number, goalTime: number, zeroColor: string): string => {
        const buckets = [
            {
                min: -Infinity,
                max: 0,
                color: zeroColor,
            },
            {
                min: 0,
                max: 0.40,
                color: '#d6e685',
            },
            {
                min: 0.40,
                max: 0.50,
                color: '#8cc665',
            },
            {
                min: 0.50,
                max: 0.90,
                color: '#44a340',
            },
            {
                min: 0.90,
                // If penalising overtime, the max is 1.125, otherwise it's infinity (ie this is the last bucket)
                max: penaliseOvertime ? 1.125 : Infinity,
                color: '#1e6823',
            },
            {
                min: 1.125,
                max: 1.25,
                color: '#f59e0b'
            },
            {
                min: 1.25,
                max: Infinity,
                color: '#ef4444'
            },
        ];

        const linearValue = value / goalTime;
        return R.find(minMax => minMax.min < linearValue && linearValue <= minMax.max, buckets)!.color;
    };

    const getWeekTotal = (weekIndex: number): number => {
        const weekStart = dFns.addWeeks(dFns.startOfWeek(startDate, {weekStartsOn: 1}), weekIndex);

        return data.filter(dv => dFns.isSameWeek(dv.date, weekStart, {weekStartsOn: 1}))
            .reduce((total, {count}) => total + count, 0);
    };

    const dateValues: DateValue[] = dFns.eachDayOfInterval({
        start: startDate,
        end: endDate,
    }).map((date) => {
        const foundData = data.find(
            ({date: dataDate}) => dFns.isEqual(dFns.startOfDay(date), dFns.startOfDay(dataDate))
        );

        return {
            date,
            count: foundData ? foundData.count : 0,
        }
    });

    return (
        <svg width="100%" height="100%" viewBox={`0 0 ${totalWidth} ${totalHeight}`} className={className}>
            {dateValues.map(({
                                 date,
                                 count
                             }) => {
                const weekIndex = dFns.differenceInCalendarWeeks(date, startDate, {weekStartsOn: 1});

                const dayOfWeek = (date.getDay() + 6) % 7;
                const x = weekIndex * (squareSize + padding);
                const y = dayOfWeek * (squareSize + padding);
                const color = getColorForValue(count, dayGoalTime, dFns.isWeekend(date) ? '#d4d4d4' : '#eeeeee');

                return (
                    <rect
                        key={date.toISOString()}
                        x={x}
                        y={y}
                        width={squareSize}
                        height={squareSize}
                        fill={color}
                        data-tooltip-id={`calendar-tooltip`}
                        data-tooltip-content={count ? `${dFns.format(date, 'EEE do')}: ${count.toFixed(2)} hours` : `${dFns.format(date, 'EEE do')}`}
                    />
                );
            })}

            {Array(numWeeks).fill(0)
                .map((_, weekIndex) => {
                    const x = weekIndex * (squareSize + padding);
                    const y = 7 * (squareSize + padding) + weekTotalPadding; // Position for the week total
                    const count = getWeekTotal(weekIndex);
                    const color = getColorForValue(count, weekGoalTime, '#eeeeee');

                    return (
                        <rect
                            key={`week-total-${weekIndex}`}
                            x={x}
                            y={y}
                            width={squareSize}
                            height={squareSize}
                            fill={color}
                            data-tooltip-id={`calendar-tooltip`}
                            data-tooltip-content={count ? `Week total ${count.toFixed(2)} hours` : ``}
                        />
                    );
                })}
        </svg>
    );
}

export default GitHubCalendarHeatmap;
