import { AppNode, nodeLabel, useEnv, useMessages, useProjectId, useStateStore } from "@/lib/state";
import { Button } from "./ui/button";
import { useCallback, useEffect, useState } from "react";
import { generateDodoConfig } from "@/lib/config";
import { useNodes, useReactFlow } from "@xyflow/react";
import { useToast } from "@/hooks/use-toast";

export function Actions() {
    const { toast } = useToast();
    const store = useStateStore();
    const projectId = useProjectId();
    const nodes = useNodes<AppNode>();
    const env = useEnv();
    const messages = useMessages();
    const instance = useReactFlow();
    const [ok, setOk] = useState(false);
    const [loading, setLoading] = useState(false);
    useEffect(() => {
        setOk(!messages.some((m) => m.type === "FATAL"));
    }, [messages, setOk]);
    const monitor = useCallback(async () => {
        const m = async function() {
            const resp = await fetch(`/api/project/${projectId}/status`, {
                method: "GET",
                headers: {
                    "Content-Type": "application/json",
                },
            })
            if (resp.status !== 200) {
                return;
            }
            const data: { type: string, name: string, status: string }[] = await resp.json();
            console.log(data);
            for (const n of nodes) {
                console.log(nodeLabel(n));
                for (const d of data) {
                    if (nodeLabel(n) === d.name) {
                        store.updateNodeData(n.id, { state: d.status });
                    }
                }
            }
            setTimeout(m, 1000);
        };
        setTimeout(m, 100);
    }, [projectId, nodes]);
    const deploy = useCallback(async () => {
        if (projectId == null) {
            return;
        }
        setLoading(true);
        try {
            const config = generateDodoConfig(nodes, env);
            if (config == null) {
                throw new Error("MUST NOT REACH!");
            }
            const resp = await fetch(`/api/project/${projectId}/deploy`, {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    state: JSON.stringify(instance.toObject()),
                    config,
                }),
            });
            if (resp.ok) {
                toast({
                    title: "Deployment succeeded",
                });
                monitor();
            } else {
                toast({
                    variant: "destructive",
                    title: "Deployment failed",
                    description: await resp.text(),
                });
            }            
        } catch (e) {
            console.log(e);
            toast({
                variant: "destructive",
                title: "Deployment failed",
            });
        } finally {
            setLoading(false);
        }
    }, [projectId, instance, nodes, env, setLoading]);
    const [st, setSt] = useState<string>();
    const save = useCallback(async () => {
        if (projectId == null) {
            return;
        }
        const resp = await fetch(`/api/project/${projectId}/saved`, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify(instance.toObject()),
        });
        if (resp.ok) {
            toast({
                title: "Save succeeded",
            });
        } else {
            toast({
                variant: "destructive",
                title: "Save failed",
                description: await resp.text(),
            });
        }            
    }, [projectId, instance, setSt]);
    const restoreSaved = useCallback(async () => {
        if (projectId == null) {
            return;
        }
        const resp = await fetch(`/api/project/${projectId}/saved`, {
            method: "GET",
        });
        const inst = await resp.json();
        const { x = 0, y = 0, zoom = 1 } = inst.viewport;
        store.setNodes(inst.nodes || []);
        store.setEdges(inst.edges || []);
        instance.setViewport({ x, y, zoom });
    }, [projectId, instance, st]);
    const clear = useCallback(() => {
        store.setEdges([]);
        store.setNodes([]);
        instance.setViewport({ x: 0, y: 0, zoom: 1 });
    }, [store]);
    // TODO(gio): Update store
    const deleteProject = useCallback(async () => {
        if (projectId == null) {
            return;
        }
        const resp = await fetch(`/api/project/${projectId}`, {
            method: "DELETE",
        });
        if (resp.ok) {
            clear();
            store.setProject(undefined);
            toast({
                title: "Save succeeded",
            });
        } else {
            toast({
                variant: "destructive",
                title: "Save failed",
                description: await resp.text(),
            });
        }            
    }, [store, clear]);
    const [props, setProps] = useState({});
    useEffect(() => {
        if (loading) {
            setProps({ loading: true });
        } else if (ok) {
            setProps({ disabled: false });
        } else {
            setProps({ disabled: true });
        }
    }, [ok, loading, setProps]);
    return (
        <>
            <Button onClick={deploy} {...props}>Deploy</Button>
            <Button onClick={save}>Save</Button>
            <Button onClick={restoreSaved}>Restore</Button>
            <Button onClick={clear} variant="destructive">Clear</Button>
            <Button onClick={deleteProject} variant="destructive" disabled={projectId === undefined}>Delete</Button>
        </>
    )
}