"use client";
import { useEffect, useState, useRef } from "react";
import { toast } from "@/components/ui/use-toast";
import { api } from "@/lib/trpc";
import { zodResolver } from "@hookform/resolvers/zod";
import { TRPCClientError } from "@trpc/client";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { ZMeasurement, zMeasurementSchema, ZMetric } from "@lifetracker/shared/types/metrics";
import { ZHour } from "@lifetracker/shared/types/days";

import { Icon } from "@/components/ui/icon";
import { titleCase } from "title-case";
import { Dialog, DialogClose, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { Trash, X } from "lucide-react";
import { useDecrementCount, useDeleteMeasurement, useIncrementCount, useSetValue, } from "@lifetracker/shared-react/hooks/measurements";
import { Separator } from "@radix-ui/react-dropdown-menu";
import { EditableText } from "../EditableText";
import spacetime from "spacetime";

interface CreateMeasurementSchema {
    type: string;
    dayId: string;
    hourId: string;
    metricId: string;
}

export default function HourMeasurementsDialog({
    hour: initialHour,
    metrics,
    reload,
    children,
}: {
    hour: ZHour,
    metrics: ZMetric[],
    reload: (newHour: ZHour) => void,
    children?: React.ReactNode;
}) {
    const [hour, setHour] = useState(initialHour);
    const [isOpen, onOpenChange] = useState(false);
    const [pendingMeasurement, setPendingMeasurement] = useState<Boolean | ZMeasurement>(false);
    const pendingRef = useRef<HTMLInputElement>(null);

    useEffect(() => {
        pendingRef.current?.focus();
    }, [pendingMeasurement]);

    const addMeasurement = (measurement: CreateMeasurementSchema) => {
        const { type, ...rest } = measurement;
        // Use a different function based on type of measurement.
        // If it's a timeseries, open a sub dialog to get the value
        // Use a switch statement to determine which function to call

        // Javascript case/switch:
        switch (type) {
            case "timeseries":
                setPendingMeasurement(rest);
                pendingRef.current?.focus();
                break;
            default:
                increment(rest);
        }
    }


    const { mutate: setValue } = useSetValue({
        onSuccess: (res) => {
            const oldMeasurement = hour.measurements.find(m => m.metricId === res.metricId);
            const newHour = {
                ...hour,
                measurements: oldMeasurement ? hour.measurements.map(m => m.metricId === res.metricId ? res : m) : [...hour.measurements, res],
            };
            setHour(newHour);
            reload(newHour);
            toast({
                description: "Measurement added!",
            });
        },
        onError: (error) => {
            toast({
                description: error.message,

            });
        },
    });

    const { mutate: increment } = useIncrementCount({
        onSuccess: (res) => {
            const oldMeasurement = hour.measurements.find(m => m.metricId === res.metricId);
            const newHour = {
                ...hour,
                measurements: oldMeasurement ? hour.measurements.map(m => m.metricId === res.metricId ? res : m) : [...hour.measurements, res],
            };
            setHour(newHour);
            reload(newHour);
            toast({
                description: "Measurement added!",
            });
        },
        onError: (error) => {
            toast({
                description: error.message,

            });
        },
    });

    const { mutate: deleteMeasurement } = useDeleteMeasurement({
        onSuccess: (_res, req) => {
            const newHour = {
                ...hour,
                measurements: hour.measurements.filter(m => m.metricId !== req.metricId),
            };
            setHour(newHour);
            reload(newHour);
            toast({
                description: "Measurement deleted!",
            });
        },
        onError: (error) => {
            toast({
                description: error.message,

            });
        },
    });



    const { mutate: decrement } = useDecrementCount({
        onSuccess: (res, req) => {
            const oldMeasurementIndex = hour.measurements.findIndex(m => m.metricId === req.metricId);
            let newMeasurements;

            if (oldMeasurementIndex !== -1) {
                if (res === undefined) {
                    // Remove the measurement if res is undefined
                    newMeasurements = [
                        ...hour.measurements.slice(0, oldMeasurementIndex),
                        ...hour.measurements.slice(oldMeasurementIndex + 1)
                    ];
                } else {
                    // Update the measurement
                    newMeasurements = [
                        ...hour.measurements.slice(0, oldMeasurementIndex),
                        res,
                        ...hour.measurements.slice(oldMeasurementIndex + 1)
                    ];
                }
            } else {
                // Add the new measurement
                newMeasurements = [...hour.measurements, res];
            }

            const newHour = {
                ...hour,
                measurements: newMeasurements,
            };

            setHour(newHour);
            reload(newHour);
            toast({
                description: res === undefined ? "Measurement removed!" : "Measurement updated!",
            });
        }
    });

    let currentMeasurements = hour.measurements.map(measurement => measurement.metricName);

    // With useEffect, update currentMeasurements when hour changes
    useEffect(() => {
        currentMeasurements = hour.measurements.map(measurement => measurement.metricName);
    }, [hour]);

    function groupMetricsByType(list: ZMetric[]): Record<string, ZMetric[]> {
        return list.reduce<Record<string, ZMetric[]>>((accumulator, item) => {
            // Initialize the array for this type if it doesn't exist yet
            if (!accumulator[item.type]) {
                accumulator[item.type] = [];
            }
            accumulator[item.type].push(item);
            return accumulator;
        }, {});
    }

    // const metricsByType = groupMetricsByType(metrics);
    const metricsByType = groupMetricsByType(metrics.filter(m => { return !currentMeasurements.includes(m.name) }));

    return (
        <Dialog open={isOpen} onOpenChange={onOpenChange}>
            <DialogTrigger asChild>{children}</DialogTrigger>
            <DialogContent>
                <DialogHeader>
                    <DialogTitle>Metrics for <span className={"secondary"}>{hour.date}</span> at {spacetime(hour.datetime).format("{hour} {ampm}")}</DialogTitle>
                </DialogHeader>
                <Separator />
                {(hour.measurements && hour.measurements.length > 0) || pendingMeasurement ?
                    (<>
                        {/* <div className="font-bold">Measurements</div> */}
                        <div className="mx-4 mb-4">
                            {hour.measurements!.map(measurement => {
                                const metric = metrics.find(m => m.id === measurement.metricId);
                                if (metric!.type === "timeseries") {
                                    return (
                                        <div key={measurement.id} className="flex items-center justify-between">
                                            <div className="gap-4 flex"><Icon name={titleCase(metric!.icon)} size={24} />
                                                <div>{metric!.name}</div></div>
                                            <div className="flex  gap-4 items-center">
                                                <EditableText
                                                    isSaving={false}
                                                    originalText={measurement.value + " " + metric?.unit} onSave={(value) => {
                                                        setValue({
                                                            metricId: metric!.id,
                                                            hourId: hour.id!,
                                                            dayId: hour.dayId,
                                                            value: parseFloat(value!),
                                                        });
                                                    }} />
                                                <Trash size={16} color="red" onClick={() => {
                                                    deleteMeasurement({ metricId: metric!.id!, hourId: hour.id! });
                                                }} />

                                            </div>
                                        </div>
                                    );
                                }
                                else {
                                    return (
                                        <div key={measurement.id} className="flex items-center justify-between">
                                            <div className="gap-4 flex"><Icon name={titleCase(metric.icon)} size={24} />
                                                <div>{metric.name}</div></div>
                                            <div className="flex  gap-4 items-center">
                                                <div className="hover:cursor-pointer" onClick={() => {
                                                    decrement({ metricId: metric.id, hourId: hour.id, dayId: hour.dayId });
                                                }} ><Icon name={"Minus"} /></div>
                                                <div>{measurement.value}</div>
                                                <div className="hover:cursor-pointer" onClick={() => {
                                                    increment({ metricId: metric.id, hourId: hour.id, dayId: hour.dayId });
                                                }}><Icon name={"Plus"} /></div>
                                            </div>
                                        </div>
                                    );
                                }
                            })}

                            {pendingMeasurement && (
                                <div className="flex items-center justify-between">
                                    <div className="gap-4 flex"><Icon name={titleCase(pendingMeasurement.icon)} size={24} />
                                        <div>{metrics.find(m => m.id === pendingMeasurement.metricId)?.name}</div></div>
                                    <div className="flex  gap-4 items-center">
                                        <div>
                                            <input
                                                ref={pendingRef}
                                                type="text" className="border border-gray-300 rounded-md p-1 mr-1 w-16 text-right"
                                                value={pendingMeasurement.value}
                                                onKeyDown={(e) => {
                                                    if (e.key === "Enter") {
                                                        setValue({
                                                            ...pendingMeasurement,
                                                            value: parseFloat(pendingRef.current.value),
                                                        });
                                                        setPendingMeasurement(false);
                                                    }
                                                }}
                                            />
                                            &nbsp;
                                            {pendingMeasurement.unit}
                                        </div>
                                    </div>
                                </div>
                            )
                            }
                        </div>
                    </>)
                    : <>
                    </>
                }

                {Object.keys(metricsByType).map(type => (
                    <div key={`metric-${type}`} className="">
                        <div className="font-bold border-b border-white">{titleCase(type)}</div>
                        <div className="grid mt-3" style={{ gridTemplateColumns: "repeat(4, 1fr)", rowGap: "1em" }}>
                            {metricsByType[type].map(metric => (
                                // If metric.name is in currentMeasurements, don't show it
                                currentMeasurements.includes(metric.name) ? null :
                                    <button style={{}} key={metric.id} className="flex flex-col items-center justify-center hover:opacity-50" onClick={() => {
                                        addMeasurement({ ...metric, metricId: metric.id!, hourId: hour.id!, dayId: hour.dayId });
                                        // onOpenChange(false);
                                    }}>
                                        <Icon name={titleCase(metric.icon)} size={32} />
                                        <span className="text-sm">{metric.name}</span>
                                    </button>
                            ))}
                        </div>
                    </div>
                ))}
            </DialogContent>
        </Dialog>
    );
}
