import {
  Tree as DNDTree,
  DndProvider,
  NodeModel,
  PlaceholderRender,
  getBackendOptions,
} from "@minoru/react-dnd-treeview";
import { FC, useState } from "react";
import { HTML5Backend } from "react-dnd-html5-backend";
import { useGlobal, useLocal } from "web-utils";
import { IItem } from "../../../../utils/types/item";
import { FMCompDef } from "../../../../utils/types/meta-fn";
import { EDGlobal, IMeta, active } from "../../logic/ed-global";
import { treeRebuild } from "../../logic/tree/build";
import { propPopover } from "./prop-master/prop-form";
import { EdPropCompTreeItem, PropItem } from "./prop-master/tree-item";
import { Popover } from "../../../../utils/ui/popover";
import { SimpleMonaco } from "./simple-monaco";

const propRef = {
  el: null as any,
};

export const EdSidePropComp: FC<{ meta: IMeta }> = ({ meta }) => {
  const p = useGlobal(EDGlobal, "EDITOR");
  const item = meta?.item as IItem;
  const local = useLocal({
    json: "",
    typings: "",
    openJSON: false,
    openTypings: false,
  });
  const [_, set] = useState({});
  const render = () => {
    set({});
  };
  propPopover.render = render;
  if (!item) return null;

  const TypedTree = DNDTree<PropItem>;

  let filtered = [] as NodeModel<PropItem>[];
  let mprops = meta.mitem?.get("component")?.get("props");
  const mcomp = meta.mitem?.get("component");

  if (!mprops) {
    if (mcomp) {
      mcomp.set("props", new Y.Map() as any);
      mprops = mcomp.get("props");
    }
  }

  if (mprops && meta.mitem) {
    mprops.forEach((m, key) => {
      filtered.push({
        id: `${item.id}-${key}`,
        parent: "root",
        text: key,
        droppable: true,
        data: {
          mitem: meta.mitem as any,
          prop: m.toJSON() as any,
          mprop: m,
          name: key,
        },
      });
    });

    filtered = filtered.sort((a, b) => {
      if (a.data?.name.startsWith("new_prop")) return 1;
      if (b.data?.name.startsWith("new_prop")) return -1;
      if (
        a.data &&
        b.data &&
        typeof a.data.prop.idx !== "undefined" &&
        typeof b.data.prop.idx !== "undefined"
      ) {
        return a.data.prop.idx - b.data.prop.idx;
      }
      return 0;
    });
  }

  const grouped: Record<string, NodeModel<PropItem>[]> = {};
  for (const item of filtered) {
    let [group, ...names] = item.text.split("__");
    let name = names.join("_");

    if (!name && !item.text.endsWith("__")) {
      name = group;
      group = "_";
    }

    if (!grouped[group]) {
      grouped[group] = [];
    }

    grouped[group].push(item);
  }

  let final_tree: NodeModel<PropItem>[] = [];
  let root_tree: NodeModel<PropItem>[] = [];
  for (const items of Object.values(grouped)) {
    const idx = items.findIndex((e) => e.text.endsWith("__"));
    const plucked = items.splice(idx, 1);
    items.unshift(plucked[0]);
    for (const item of items) {
      if (item.data?.name.includes("__")) {
        final_tree.push(item);
      } else {
        root_tree.push(item);
      }
    }
  }

  root_tree = root_tree.sort((a, b) => {
    return (a.data?.prop?.idx || 0) - (b.data?.prop?.idx || 0);
  });
  final_tree = [...root_tree, ...final_tree];

  let hide = localStorage.getItem("prasi-prop-hide")?.split(",") || [];

  return (
    <div className="flex flex-col text-[12px] flex-1">
      <div className="flex border-b p-1 h-[28px] items-center bg-slate-50 justify-between select-none">
        <div
          className="border flex items-center cursor-pointer bg-white hover:bg-blue-100 space-x-[1px] pr-[5px]"
          onClick={() => {
            p.ui.side.prop = false;
            p.render();
          }}
        >
          <div
            className={css`
              margin-bottom: 1px;
            `}
            dangerouslySetInnerHTML={{
              __html: `
          <svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 4L9 11L4.5 7.5L9 4Z" fill="currentColor"></path></svg>`,
            }}
          ></div>
          <div>Style</div>
        </div>

        <div
          className={cx(
            "flex items-center space-x-1 cursor-pointer",
            !!item.component?.useStyle &&
              "border border-green-600 px-1 rounded-sm text-green-700"
          )}
          onClick={() => {
            const useStyle = !!meta.mitem?.get("component")?.get("useStyle");
            meta.mitem?.get("component")?.set("useStyle", !useStyle);
            treeRebuild(p);
            p.render();
          }}
        >
          {!item.component?.useStyle ? <ToggleOff /> : <ToggleOn />}
          <div className={cx("mt-[2px]")}>Inherit Style</div>
        </div>
        <div
          className="border pl-1 cursor-pointer bg-white hover:bg-blue-100 flex items-center"
          onClick={() => {
            if (active.comp_id) {
              active.comp_id = active.instance.comp_id || "";
              active.item_id = active.instance.item_id || "";
              active.instance.comp_id = "";
              active.instance.item_id = "";
              p.render();
            }
          }}
        >
          <div>Instance</div>
          <div
            className={css`
              margin-bottom: 1px;
            `}
            dangerouslySetInnerHTML={{
              __html: `
              <svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6 11L6 4L10.5 7.5L6 11Z" fill="currentColor"></path></svg>`,
            }}
          ></div>
        </div>
      </div>
      <div className="flex flex-1 relative overflow-y-auto overflow-x-hidden">
        <div
          className={cx("absolute inset-0")}
          ref={(ref) => (propRef.el = ref)}
        >
          <DndProvider
            backend={HTML5Backend}
            options={getBackendOptions({
              html5: {
                rootElement: propRef.el,
              },
            })}
          >
            <TypedTree
              tree={final_tree}
              sort={false}
              onDrop={(tree, { dragSourceId, relativeIndex }) => {
                const doc = meta.mitem?.doc;
                doc?.transact(() => {
                  mprops?.forEach((p, key) => {
                    const idx = tree.findIndex(
                      (e) => `${item.id}-${key}` === e.id
                    );
                    if (idx >= 0) {
                      p.set("idx", idx);
                    }
                  });
                });
              }}
              render={(node, params) => (
                <EdPropCompTreeItem
                  node={node}
                  render={render}
                  params={params}
                  hidden={
                    node.text.includes("__") &&
                    hide.includes(node.text.split("__")[0])
                  }
                  toggleExpand={(node) => {
                    const group = node.text.split("__")[0];
                    if (hide.includes(group)) {
                      hide = hide.filter((e) => e !== group);
                    } else {
                      hide.push(group);
                    }

                    localStorage.setItem("prasi-prop-hide", hide?.join(","));
                    local.render();
                  }}
                />
              )}
              rootId={"root"}
              classes={treeClasses}
              dropTargetOffset={10}
              initialOpen={true}
              placeholderRender={(node, params) => (
                <Placeholder node={node} params={params} />
              )}
              dragPreviewRender={() => <></>}
            />
          </DndProvider>
          <div className="flex">
            <div
              className="m-1 border border-blue-200 px-2 self-start text-[13px] hover:bg-blue-100 cursor-pointer select-none flex-1 h-[22px]"
              onClick={() => {
                if (mprops) {
                  const indexes: (number | undefined)[] = [];
                  mprops.forEach((e) => indexes.push(e.get("idx")));
                  let idx: any = (indexes.sort().pop() || 0) + 1;
                  if (indexes.length === 0) {
                    idx = 1;
                  } else {
                    idx = parseInt(idx) + 1;
                  }

                  let name = `new_prop_${idx}`;
                  while (mprops.get(name)) {
                    idx = parseInt(idx) + 1;
                    name = `new_prop_${idx}`;
                  }

                  const map = new Y.Map() as FMCompDef;
                  syncronize(map, {
                    idx,
                    name,
                    type: "string",
                    value: '"hello"',
                    valueBuilt: '"hello"',
                    meta: {
                      type: "text",
                    },
                  });
                  mprops.set(name, map);
                  propPopover.name = name;
                  p.render();
                }
              }}
            >
              + New Prop
            </div>{" "}
            <Popover
              content={
                <div
                  className={cx(css`
                    width: 700px;
                    height: 500px;
                    margin: 5px 0px;
                  `)}
                >
                  <SimpleMonaco
                    onChange={(value) => {
                      local.typings = value;
                      local.render();
                    }}
                    value={local.typings}
                    lang="typescript"
                  />
                </div>
              }
              open={local.openTypings}
              onOpenChange={(open) => {
                try {
                  if (mcomp) {
                    if (!open) {
                      mcomp.set("typings", local.typings);
                      treeRebuild(p);
                      p.render();
                    } else {
                      local.typings =
                        mcomp.get("typings") ||
                        `\
const typings = {
  _raw: {
  }
}`;
                    }
                  }
                } catch (e) {
                  console.log(e);
                }
                local.openTypings = open;
                local.render();
              }}
            >
              <div className="m-1 ml-0 border border-blue-200 px-2 self-start text-[13px] hover:bg-blue-100 cursor-pointer select-none flex items-center space-x-1 text-xs h-[22px]">
                <span>Types</span>
              </div>
            </Popover>
            <Popover
              content={
                <div
                  className={cx(css`
                    width: 700px;
                    height: 500px;
                    margin: 5px 0px;
                  `)}
                >
                  <SimpleMonaco
                    onChange={(value) => {
                      local.json = value;
                      local.render();
                    }}
                    value={local.json}
                    lang="json"
                  />
                </div>
              }
              open={local.openJSON}
              onOpenChange={(open) => {
                try {
                  if (!open) {
                    syncronize(mprops as any, JSON.parse(local.json));
                    treeRebuild(p);
                    p.render();
                  } else {
                    local.json = JSON.stringify(mprops?.toJSON(), null, 2);
                  }
                } catch (e) {
                  console.log(e);
                }
                local.openJSON = open;
                local.render();
              }}
            >
              <div className="m-1 ml-0 border border-blue-200 px-2 self-start text-[13px] hover:bg-blue-100 cursor-pointer select-none flex items-center space-x-1 text-xs h-[22px]">
                <span>JSON</span>
              </div>
            </Popover>
          </div>
        </div>
      </div>
    </div>
  );
};

const treeClasses = {
  container: "flex flex-col flex-1",
  dropTarget: "drop-target",
  placeholder: "placeholder",
  draggingSource: css`
    background: #e4f0ff;
    cursor: not-allowed;
  `,
};

export const Placeholder: FC<{
  node: Parameters<PlaceholderRender<PropItem>>[0];
  params: Parameters<PlaceholderRender<PropItem>>[1];
}> = ({ params }) => {
  return (
    <div
      className={cx(
        "flex items-center bg-blue-50",
        css`
          height: 10px;
          z-index: 99;
          position: absolute;
          left: 0px;
          transform: translateY(-50%);
          right: 0px;
        `
      )}
    >
      <div
        className={cx(
          "flex-1",
          css`
            background-color: #1b73e8;
            height: 2px;
          `
        )}
      ></div>
    </div>
  );
};

const ToggleOn = () => (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="20"
    version="1.1"
    viewBox="0 0 122.878 73.391"
    xmlSpace="preserve"
  >
    <path
      fillRule="evenodd"
      fill="currentColor"
      d="M35.691 0h51.495c19.631 0 35.691 16.513 35.691 36.695S106.815 73.39 87.186 73.39H35.691C16.062 73.391 0 56.877 0 36.695S16.061 0 35.691 0zm50.492 7.863c16.184 0 29.309 13.125 29.309 29.309S102.366 66.48 86.183 66.48 56.874 53.355 56.874 37.172 69.999 7.863 86.183 7.863z"
      clipRule="evenodd"
    ></path>
  </svg>
);

const ToggleOff = () => (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="20"
    x="0"
    y="0"
    version="1.1"
    viewBox="0 0 122.882 73.393"
    xmlSpace="preserve"
  >
    <path
      fillRule="evenodd"
      d="M87.189 0H35.694C16.063 0 0 16.513 0 36.697c0 20.182 16.063 36.695 35.694 36.695H87.19c19.632 0 35.692-16.514 35.692-36.695C122.882 16.513 106.821 0 87.189 0zM59.367 6.057a37.56 37.56 0 014.846 4.098l.022.022.073.07a37.324 37.324 0 012.621 2.939 37.495 37.495 0 018.288 23.349c.012.068.014.141.014.213V36.834l-.002.039v.021l-.012.061a37.491 37.491 0 01-3.959 16.615 37.634 37.634 0 01-7.046 9.777l-.021.02c-.311.311-.671.652-1.05 1.008a37.606 37.606 0 01-3.637 2.957h25.974l.18-.008h.1l.032.002h.029l.029.006a28.57 28.57 0 0013.018-3.221 30.04 30.04 0 007.72-5.742l.012-.012-.003-.004.023-.023.052-.049a29.115 29.115 0 002.08-2.389l.002-.002-.002-.002a30.487 30.487 0 001.882-2.678 31.075 31.075 0 004.722-16.381l-.01-.129v-.012l.005-.033v-.021l.002-.045v-.016l.007-.045a31.218 31.218 0 00-2.343-11.718 30.9 30.9 0 00-6.427-9.785L106.57 15l-.003.002-.011-.012h-.003l-.02-.021-.01-.016a29.961 29.961 0 00-2.338-2.146 29.773 29.773 0 00-2.594-1.927c-4.57-3.02-9.999-4.791-15.791-4.818l-.139.007h-.002l-.027-.004h-.09l-.027-.007H59.367v-.001zM36.696 7.864c-16.183 0-29.308 13.124-29.308 29.308 0 16.186 13.125 29.311 29.308 29.311 16.185 0 29.311-13.125 29.311-29.311 0-16.184-13.126-29.308-29.311-29.308z"
      clipRule="evenodd"
    ></path>
  </svg>
);
