import { v4 as uuidv4 } from "uuid";
import { useStateStore, AppNode, GatewayHttpsNode, ServiceNode, nodeLabel, useEnv, nodeIsConnectable } from '@/lib/state';
import { Handle, Position, useNodes } from '@xyflow/react';
import { NodeRect } from './node-rect';
import { useCallback, useEffect, useMemo } from 'react';
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm, EventType, DeepPartial } from 'react-hook-form';
import { Form, FormControl, FormField, FormItem, FormMessage } from './ui/form';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Checkbox } from "./ui/checkbox";
import { Label } from "./ui/label";
import { Button } from "./ui/button";
import { XIcon } from "lucide-react";

const schema = z.object({
  network: z.string().min(1, "reqired"),
  subdomain: z.string().min(1, "required"),
});

const connectedToSchema = z.object({
  id: z.string(),
  portId: z.string(),
});

const authEnabledSchema = z.object({
  enabled: z.boolean(),
});

const authGroupSchema = z.object({
  group: z.string(),
});

const authNoAuthPatternSchema = z.object({
  noAuthPathPattern: z.string(),
});

export function NodeGatewayHttps(node: GatewayHttpsNode) {
  const { id, selected } = node;
  const isConnectableNetwork = useMemo(() => nodeIsConnectable(node, "subdomain"), [node]);
  const isConnectable = useMemo(() => nodeIsConnectable(node, "https"), [node]);
  return (
    <NodeRect id={id} selected={selected} type={node.type} state={node.data.state}>
      {nodeLabel(node)}
      <Handle
        type={"source"}
        id="subdomain"
        position={Position.Top}
        isConnectable={isConnectableNetwork}
        isConnectableStart={isConnectableNetwork}
        isConnectableEnd={isConnectableNetwork}
      />
      <Handle
        type={"target"}
        id="https"
        position={Position.Bottom}
        isConnectable={isConnectable}
        isConnectableStart={isConnectable}
        isConnectableEnd={isConnectable}
      />
    </NodeRect>
  );
}

export function NodeGatewayHttpsDetails({ id, data }: GatewayHttpsNode) {
  const store = useStateStore();
  const env = useEnv();
  const form = useForm<z.infer<typeof schema>>({
    resolver: zodResolver(schema),
    mode: "onChange",
    defaultValues: {
      network: data.network,
      subdomain: data.subdomain,
    },
  });
  useEffect(() => {
    const sub = form.watch((value: DeepPartial<z.infer<typeof schema>>, { name }: { name?: keyof z.infer<typeof schema> | undefined, type?: EventType | undefined }) => {
      if (name === "network") {
        let edges = store.edges;
        if (data.network !== undefined) {
          edges = edges.filter((e) => {
            if (e.source === id && e.sourceHandle === "subdomain" && e.target === data.network && e.targetHandle === "subdomain") {
              return false;
            } else {
              return true;
            }
          });
        }
        if (value.network !== undefined) {
          edges = edges.concat({
            id: uuidv4(),
            source: id,
            sourceHandle: "subdomain",
            target: value.network,
            targetHandle: "subdomain",
          });
        }
        store.setEdges(edges);
        store.updateNodeData<"gateway-https">(id, { network: value.network });
      } else if (name === "subdomain") {
        store.updateNodeData<"gateway-https">(id, { subdomain: value.subdomain });
      }
    });
    return () => sub.unsubscribe();
  }, [id, data, form, store]);
  const connectedToForm = useForm<z.infer<typeof connectedToSchema>>({
    resolver: zodResolver(connectedToSchema),
    mode: "onChange",
    defaultValues: {
      id: data.https?.serviceId,
      portId: data.https?.portId,
    },
  });
  useEffect(() => {
    connectedToForm.reset({
      id: data.https?.serviceId,
      portId: data.https?.portId,
    });
  }, [connectedToForm, data]);
  const nodes = useNodes<AppNode>();
  const selected = useMemo(() => {
    if (data !== undefined && data.https !== undefined) {
      const https = data.https;
      return nodes.find((n) => n.id === https.serviceId)! as ServiceNode;
    }
    return null;
  }, [data]);
  const selectable = useMemo(() => {
    return nodes.filter((n) => {
      if (n.id === id) {
        return false;
      }
      if (selected !== null && selected.id === id) {
        return true;
      }
      if (n.type !== "app") {
        return false;
      }
      return n.data && n.data.ports && n.data.ports.length > 0;
    })
  }, [nodes, selected]);
  useEffect(() => {
    const sub = connectedToForm.watch((value: DeepPartial<z.infer<typeof connectedToSchema>>, { name, type }: { name?: keyof z.infer<typeof connectedToSchema> | undefined, type?: EventType | undefined }) => {
      if (type !== "change") {
        return;
      }
      switch (name) {
        case "id":
          if (!value.id) {
            break;
          }
          const current = store.edges.filter((e) => e.target === id);
          const cid = current[0] ? current[0].id : undefined;
          store.replaceEdge({
            source: value.id,
            sourceHandle: "ports",
            target: id,
            targetHandle: "https",
          }, cid);
          break;
        case "portId":
          store.updateNodeData<"gateway-https">(id, {
            https: {
              serviceId: value.id,
              portId: value.portId,
            }
          });
          break;
      }
    });
    return () => sub.unsubscribe();
  }, [connectedToForm, store, selectable]);
  const authEnabledForm = useForm<z.infer<typeof authEnabledSchema>>({
    resolver: zodResolver(authEnabledSchema),
    mode: "onChange",
    defaultValues: {
      enabled: data.auth ? data.auth.enabled : false,
    },
  });
  const authGroupForm = useForm<z.infer<typeof authGroupSchema>>({
    resolver: zodResolver(authGroupSchema),
    mode: "onSubmit",
    defaultValues: {
      group: "",
    },
  }); const authNoAuthPatternFrom = useForm<z.infer<typeof authNoAuthPatternSchema>>({
    resolver: zodResolver(authNoAuthPatternSchema),
    mode: "onChange",
    defaultValues: {
      noAuthPathPattern: "",
    },
  });
  useEffect(() => {
    const sub = authEnabledForm.watch((value, { name }) => {
      if (name === "enabled") {
        store.updateNodeData<"gateway-https">(id, {
          auth: {
            ...data.auth,
            enabled: value.enabled,
          }
        })
      }
    });
    return () => sub.unsubscribe();
  }, [id, data, authEnabledForm, store]);
  const removeGroup = useCallback((group: string) => {
    const groups = data?.auth?.groups || [];
    store.updateNodeData<"gateway-https">(id, {
      auth: {
        ...data.auth,
        groups: groups.filter((g) => g !== group),
      },
    });
    return true;
  }, [id, data, store]);
  const onGroupSubmit = useCallback((values: z.infer<typeof authGroupSchema>) => {
    const groups = data.auth?.groups || [];
    groups.push(values.group)
    store.updateNodeData<"gateway-https">(id, {
      auth: {
        ...data.auth,
        groups,
      },
    });
    authGroupForm.reset();
  }, [id, data, store]);
  const removeNoAuthPathPattern = useCallback((path: string) => {
    const noAuthPathPatterns = data?.auth?.noAuthPathPatterns || [];
    store.updateNodeData<"gateway-https">(id, {
      auth: {
        ...data.auth,
        noAuthPathPatterns: noAuthPathPatterns.filter((p) => p !== path),
      },
    });
    return true;
  }, [id, data, store]);
  const onNoAuthPathPatternSubmit = useCallback((values: z.infer<typeof authNoAuthPatternSchema>) => {
    const noAuthPathPatterns = data.auth?.noAuthPathPatterns || [];
    noAuthPathPatterns.push(values.noAuthPathPattern)
    store.updateNodeData<"gateway-https">(id, {
      auth: {
        ...data.auth,
        noAuthPathPatterns,
      },
    });
    authNoAuthPatternFrom.reset();
  }, [id, data, store]);
  return (
    <>
      <Form {...form}>
        <form className="space-y-2">
          <FormField
            control={form.control}
            name="network"
            render={({ field }) => (
              <FormItem>
                <Select onValueChange={field.onChange} defaultValue={field.value}>
                  <FormControl>
                    <SelectTrigger>
                      <SelectValue placeholder="Network" />
                    </SelectTrigger>
                  </FormControl>
                  <SelectContent>
                    {env.networks.map((n) => (
                      <SelectItem key={n.name} value={n.domain}>{`${n.name} - ${n.domain}`}</SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <FormMessage />
              </FormItem>
            )}
          />
          <FormField
            control={form.control}
            name="subdomain"
            render={({ field }) => (
              <FormItem>
                <FormControl>
                  <Input placeholder="subdomain" className="border border-black" {...field} />
                </FormControl>
                <FormMessage />
              </FormItem>
            )}
          />
        </form>
      </Form>
      <Form {...connectedToForm}>
        <form className="space-y-2">
          <FormField
            control={connectedToForm.control}
            name="id"
            render={({ field }) => (
              <FormItem>
                <Select onValueChange={field.onChange} defaultValue={field.value}>
                  <FormControl>
                    <SelectTrigger>
                      <SelectValue placeholder="Service" />
                    </SelectTrigger>
                  </FormControl>
                  <SelectContent>
                    {selectable.map((n) => (
                      <SelectItem key={n.id} value={n.id}>{nodeLabel(n)}</SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <FormMessage />
              </FormItem>
            )}
          />
          <FormField
            control={connectedToForm.control}
            name="portId"
            render={({ field }) => (
              <FormItem>
                <Select onValueChange={field.onChange} defaultValue={field.value}>
                  <FormControl>
                    <SelectTrigger>
                      <SelectValue placeholder="Port" />
                    </SelectTrigger>
                  </FormControl>
                  <SelectContent>
                    {selected && selected.data.ports.map((p) => (
                      <SelectItem key={p.id} value={p.id}>{p.name} - {p.value}</SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <FormMessage />
              </FormItem>
            )}
          />
        </form>
      </Form>
      Auth
      <Form {...authEnabledForm}>
        <form className="space-y-2">
          <FormField
            control={authEnabledForm.control}
            name="enabled"
            render={({ field }) => (
              <FormItem>
                <Checkbox id="authEnabled" onCheckedChange={field.onChange} checked={field.value} />
                <Label htmlFor="authEnabled">Enabled</Label>
                <FormMessage />
              </FormItem>
            )}
          />
        </form>
      </Form>
      {data && data.auth && data.auth.enabled ? (
        <>
          Authorized Groups
          <ul>
            {(data.auth.groups || []).map((p) => (<li key={p}><Button size={"icon"} variant={"ghost"} onClick={() => removeGroup(p)}><XIcon /></Button> {p}</li>))}
          </ul>
          <Form {...authGroupForm}>
            <form className="flex flex-row space-x-1" onSubmit={authGroupForm.handleSubmit(onGroupSubmit)}>
              <FormField
                control={authGroupForm.control}
                name="group"
                render={({ field }) => (
                  <FormItem>
                    <FormControl>
                      <Input placeholder="group" className="border border-black" {...field} />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                )}
              />
              <Button type="submit">Add Group</Button>
            </form>
          </Form>
          Auth optional path patterns
          <ul>
            {(data.auth.noAuthPathPatterns || []).map((p) => (<li key={p}><Button size={"icon"} variant={"ghost"} onClick={() => removeNoAuthPathPattern(p)}><XIcon /></Button> {p}</li>))}
          </ul>
          <Form {...authNoAuthPatternFrom}>
            <form className="flex flex-row space-x-1" onSubmit={authNoAuthPatternFrom.handleSubmit(onNoAuthPathPatternSubmit)}>
              <FormField
                control={authNoAuthPatternFrom.control}
                name="noAuthPathPattern"
                render={({ field }) => (
                  <FormItem>
                    <FormControl>
                      <Input placeholder="group" className="border border-black" {...field} />
                    </FormControl>
                    <FormMessage />
                  </FormItem>
                )}
              />
              <Button type="submit">Add</Button>
            </form>
          </Form>
        </>
      ) : (<></>)}
    </>
  );
}