import React from "react";
import makeAutoObservable from "mobx-store-inheritance";
import clone from "just-clone";
import { message } from "antd";
import { NavigateFunction } from "react-router-dom";
import { v4 } from "uuid";
import { CoreError, UiDrawerFormState } from "../../../core/store/base_store";
import {
  BehaviorTreeBuilderModel,
  BtNodeView as BtNodeObserver,
  NodeRerenderObserver,
  ReteForceUpdateObserver,
  UpdateEvent,
} from "../model/editor_view";
import { NodeEditor } from "rete";
import { AreaExtra, Schemes } from "./ui/editor/editor";
import { AreaPlugin } from "rete-area-plugin";
import { NodeBehaviorTree } from "../model/node_behavior_tree";
import { BehaviorTreeBuilderHttpRepository } from "../data/behavior_tree_builder_http_repository";
import { DependencyViewModel, IParam, Skills } from "../../../core/model/skill_model";
import { ISkillView } from "./ui/skill_tree/skill_tree";
import { BehaviorTreeViewModel } from "../model/behavior_tree_view_model";
import { UiBaseError } from "../../../core/model/ui_base_error";
import { behaviorTreeBuilderStore } from "./behavior_tree_builder_screen";
import { BehaviorTreeModel } from "../model/behavior_tree_model";
import { PrimitiveViewModel, SystemPrimitive } from "../model/primitive_view_model";
import { SceneAsset } from "../../../core/model/scene_asset";
import { themeStore } from "../../..";

interface I2DArea {
  x: number;
  y: number;
  w: number;
  h: number;
}

export enum DrawerState {
  editThreadBehaviorTree = "Редактирование",
}

export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeViewModel, CoreError> {
  sceneAsset?: SceneAsset;
  viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty();
  behaviorTreeModel: BehaviorTreeModel = BehaviorTreeModel.empty();
  skillTemplates: Skills = Skills.empty();
  filledOutTemplates: Skills = Skills.empty();
  area?: I2DArea;
  btNodeObserver: BtNodeObserver = new BtNodeObserver();
  reteForceUpdateObserver?: ReteForceUpdateObserver;
  btTreeModels: BehaviorTreeModel[] = [];
  activeProject: string = "";
  behaviorTreeBuilderHttpRepository = new BehaviorTreeBuilderHttpRepository();
  canRun = true;
  isNeedSaveBtn = false;
  selected: string = "";
  selectedSid: string = "";
  nodeBehaviorTree: NodeBehaviorTree[] = [];
  navigate?: NavigateFunction;
  editor?: NodeEditor<Schemes>;
  areaPlugin?: AreaPlugin<Schemes, AreaExtra>;
  nodeUpdateObserver?: NodeRerenderObserver;
  primitiveViewModel: PrimitiveViewModel;
  skillTree: ISkillView = {
    name: "",
    children: [
      {
        name: "Действия",
        children: [],
        isSystem: true,
      },
    ],
  };

  constructor() {
    super(DrawerState);
    makeAutoObservable(this);
    this.primitiveViewModel = new PrimitiveViewModel();
    this.skillTree.children?.push({
      name: "Примитивы BT",
      children: this.primitiveViewModel.toSkillView(),
      isSystem: true,
    });
  }

  syncScene = async (editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>) => {
    this.editor = editor;
    this.areaPlugin = area;
  };
  getAllTopics = () => this.filledOutTemplates.topicsStack;
  errorHandingStrategy = (_: CoreError) => {};

  dragEnd = (e: EventTarget) => {
    if (this.canRun) {
      //@ts-expect-error
      this.drawSkillCheck(e.clientX as number, e.clientY as number, e.target.innerText as string);

      this.canRun = false;
      setTimeout(() => {
        this.canRun = true;
      }, 100);
    }
  };
  drawSkillCheck = (x: number, y: number, name: string) => {
    const drawPoint = { x: x, y: y, w: 1, h: 1 };
    if (
      drawPoint.x < this.area!.x + this.area!.w &&
      drawPoint.x + drawPoint.w > this.area!.x &&
      drawPoint.y < this.area!.y + this.area!.h &&
      drawPoint.y + drawPoint.h > this.area!.y
    ) {
      const sid = v4();
      this.btNodeObserver.emit({
        x: x,
        y: y - (this.area!.y + this.area!.h / 2),
        name: name,
        id: sid,
      });
      this.isNeedSaveBtn = true;
      if (!name.isEqualMany(Object.keys(SystemPrimitive))) {
        this.skillTemplates?.getSkill(name).fold(
          (skill) => {
            const skillModel = clone(skill).setSid(sid);
            skillModel.BTAction = skill.BTAction.filter((el) => el.name.isEqual(name));

            this.filledOutTemplates?.skills.push(skillModel);
            if (skill.emptyParam()) {
              this.filledOutTemplates.topicsStack.push(
                ...skillModel.topicsOut.updateAll({ sid: skillModel.sid ?? "" })
              );
            }
          },
          () => console.log("error")
        );
      }
    }
  };
  dispose(): void {
    this.sceneAsset = undefined;
    this.behaviorTreeModel = BehaviorTreeModel.empty();
    this.skillTemplates = Skills.empty();
    this.filledOutTemplates = Skills.empty();
    this.area = undefined;
    this.reteForceUpdateObserver = undefined;
    this.btTreeModels = [];
    this.nodeBehaviorTree = [];
    this.editor = undefined;
    this.areaPlugin = undefined;
    this.nodeUpdateObserver = undefined;
  }
  async init(navigate: NavigateFunction): Promise<void> {
    (await this.behaviorTreeBuilderHttpRepository.getActiveProjectId()).map((el) => {
      this.activeProject = el.id;
      this.viewModel.project = this.activeProject;
    });
    (await this.behaviorTreeBuilderHttpRepository.getBtSkills()).fold(
      (model) => {
        this.skillTemplates = Skills.fromSkills(model);
        this.skillTree.children = this.skillTree.children?.map((el) => {
          if (el.name === "Действия") {
            el.children = this.skillTemplates.toSkillView();
          }
          return el;
        });
      },
      (e) => console.log(e)
    );
    await this.mapOk("btTreeModels", this.behaviorTreeBuilderHttpRepository.getAllBtInstances());
    this.navigate = navigate;
  }
  initParam = async (id?: string) => {
    this.isLoading = true;

    if (id) {
      (await this.behaviorTreeBuilderHttpRepository.getBtById(id)).fold(
        async (model) => {
          this.nodeBehaviorTree = model.scene;
          this.behaviorTreeModel = model;
          if (model.skills) this.filledOutTemplates = model.skills;

          await this.mapOk("sceneAsset", this.behaviorTreeBuilderHttpRepository.getSceneAsset(model.sceneId));
          this.isLoading = false;

          this.reteForceUpdateObserver?.emit("");
        },
        async () => {
          this.errors.push(new UiBaseError(`не найдено дерево с id:${id}`));
        }
      );
    }
  };
  dragZoneSetOffset(offsetTop: number, offsetWidth: number, width: number, height: number) {
    this.area = {
      x: offsetTop,
      y: offsetWidth,
      w: width,
      h: height,
    };
  }
  onClickSaveBehaviorTree = async () =>
    this.filledOutTemplates.validation().fold(
      async () => {
        (
          await BehaviorTreeBuilderModel.fromReteScene(
            this.editor as NodeEditor<Schemes>,
            this.areaPlugin as AreaPlugin<Schemes, AreaExtra>,
            this.filledOutTemplates
          )
        ).fold(
          (xml) => {
            console.log(xml);
            this.behaviorTreeModel.skills = this.filledOutTemplates;
            this.behaviorTreeModel.scene = NodeBehaviorTree.fromReteScene(
              this.editor as NodeEditor<Schemes>,
              this.areaPlugin as AreaPlugin<Schemes, AreaExtra>
            );

            this.behaviorTreeModel.xml = xml;
            this.behaviorTreeModel.project = this.activeProject;
            this.messageHttp(this.behaviorTreeBuilderHttpRepository.editBt(this.behaviorTreeModel), {
              successMessage: "Дерево поведения сохранено",
            });
          },
          (error) => {
            message.error(`Дерево поведения ошибка: ${error}`);
            console.log(error);
          }
        );
      },
      async () => message.error(`Дерево поведения не заполнено`)
    );

  validateBt() {}

  setSelected = (label: string, selected: boolean, sid: string) => {
    this.selectedSid = sid;
    this.selected = label;

    // if (!Object.keys(SystemPrimitive).includes(label) && this.skillTemplates.skillHasForm(label)) {
    this.editDrawer(DrawerState.editThreadBehaviorTree, true);
    // }
  };
  deleteBtAction = (sid: string) => {
    this.nodeBehaviorTree = this.nodeBehaviorTree.filter((el) => !el.id.isEqual(sid));
    this.filledOutTemplates.skills = this.filledOutTemplates.deleteSid(sid);
    this.filledOutTemplates.deleteTopic(sid);
    this.nodeUpdateObserver?.emit({ type: UpdateEvent.DELETE, id: sid });

    Array.from(this.areaPlugin!.nodeViews.keys()).forEach((id) => {
      this.areaPlugin?.update("node", id);
    });
  };
  formUpdateDependency = (dependency: DependencyViewModel, formType: string) => {
    this.filledOutTemplates?.skillBySid(this.selectedSid ?? "").fold(
      (m) => {
        const model = clone(m);
        model.BTAction.forEach((action) => {
          const result: IParam[] = [];
          action.param.forEach((param) => {
            const paramClone = clone(param);
            if (param.type.isEqual(formType)) {
              paramClone.dependency = dependency;
              paramClone.isFilled = true;
            }
            result.push(paramClone);
          });
          this.filledOutTemplates.topicsStack.push(...model.topicsOut);
          action.param = result;
          return action;
        });
        this.filledOutTemplates.updateSkill(model);
        this.editDrawer(DrawerState.editThreadBehaviorTree, false);
      },
      () => console.log("UNKNOWN SID: " + this.selectedSid)
    );

    this.nodeUpdateObserver?.emit({ id: this.selectedSid, type: UpdateEvent.UPDATE });
  };
  getBodyNode(label: string, sid: string): React.ReactNode | null {
    const primitive = this.primitiveViewModel.getPrimitiveAtLabel(label);
    if (primitive) {
      return null;
    }
    return (
      <div style={{ width: "100%", display: "flex", flexDirection: "column", alignItems: "flex-start" }}>
        {this.skillTemplates?.getSkillParams(label).map((el, index) => (
          <div style={{ display: "flex", flexDirection: "row", width: "100%", marginBottom: 5 }} key={index}>
            <div
              style={{
                width: this.skillTemplates?.getSkillWidthAtLabel(label) * 10,
                marginRight: 8,
                padding: 5,
                color: behaviorTreeBuilderStore.isFilledInput(el.type, sid)
                  ? themeStore.theme.onSurface
                  : themeStore.theme.error50,
              }}
            >
              {el.type}
            </div>
            <div
              style={{
                display: "flex",
                width: 124,
                backgroundColor: themeStore.theme.outlineVariantDark,
                padding: 5,
                borderRadius: "4px 4px 0px 0px",
                borderBottom: `1px solid ${
                  behaviorTreeBuilderStore.isFilledInput(el.type, sid)
                    ? themeStore.theme.onSurface
                    : themeStore.theme.error50
                }`,
              }}
            >
              {behaviorTreeBuilderStore.isFilledInput(el.type, sid)
                ? this.filledOutTemplates.getDependencyBySkillLabelAndType(label, sid).toView(behaviorTreeBuilderStore)
                : undefined}
            </div>
          </div>
        ))}
      </div>
    );
  }
  isFilledInput = (type: string, sid: string): boolean =>
    this.filledOutTemplates?.dependencyIsFilled(type, sid) ?? false;

  getInputs(name: string) {
    const result = this.primitiveViewModel.getPrimitiveAtLabel(name);
    if (result) {
      return {
        input: result.input ? "a" : null,
        output: result.output ? "a" : null,
      };
    }
    return {
      input: "a",
      output: null,
    };
  }
  getStylesByLabelNode(label: string): React.CSSProperties | undefined {
    for (const el of this.primitiveViewModel.primitives) {
      if (el.label.isEqual(label)) return el.css;
    }
    return this.skillTemplates.skillIsCondition(label).fold(
      () => Object.assign(this.filledOutTemplates.getCssAtLabel(label), { borderRadius: 33 }),
      () => this.filledOutTemplates.getCssAtLabel(label)
    );
  }
  changeSceneViewModel = (text: string) => {};
}
