import { StickyNote, Image, Group, BaseItem, GroupableItem } from "@mirohq/websdk-types";
import { getStickiesToChopInTree, getTypedMiroItem } from "./todo-tree-helper";

export const init = async () => {
  miro.board.ui.on('icon:click', async () => {
    await miro.board.ui.openPanel({ url: 'app.html' });
  });

  await miro.board.ui.on('custom:chop-todo-tree', handleChoppingAction);
  await miro.board.experimental.action.register(
    {
      "event": "chop-todo-tree",
      "ui": {
        "label": {
          "en": "Chop TODO tree",
        },
        "icon": "scissors",
        "description": "Chop down TODO tree staring here",
      },
      "scope": "local",
      "predicate": {
        "type": "sticky_note"
      },
      "contexts": {
        "item": {}
      }
    }
  );
}

interface AxeAnimationArgs {
  axe: Image,
  stickyNote: StickyNote,
  onChopped: () => void
}

type RotatableItem = BaseItem & { rotation: number };

interface ItemState {
  item: GroupableItem;
  x: number;
  y: number;
  rotation: number;
  deltaX: number;
  deltaY: number;
}

const MOVABLE_TYPES = ["card", "shape", "frame", "image", "text", "app_card", "sticky_note"];
const ROTATABLE_TYPES = ["card", "shape", "image", "text", "app_card", "sticky_note"];

const isMovable = (item: GroupableItem) => MOVABLE_TYPES.includes(item.type);
const isRotatable = (item: GroupableItem) => ROTATABLE_TYPES.includes(item.type);

const updateItemPosition = async (state: ItemState, x: number, y: number, rotation?: number) => {
  if (!isMovable(state.item))
    return;
  
  (state.item as BaseItem).x = x;
  (state.item as BaseItem).y = y;
  if (rotation !== undefined && isRotatable(state.item)) {
    (state.item as RotatableItem).rotation = rotation;
  }
  await state.item.sync();
};

const playAxeAnimation = async ({axe, stickyNote, onChopped}: AxeAnimationArgs) => {
  const groupItems = axe.groupId 
    ? await (await getTypedMiroItem<Group>(axe.groupId, "group"))?.getItems() ?? [axe]
    : [axe];
  
  const initialStates: ItemState[] = groupItems.map(item => ({
    item,
    x: (item as BaseItem).x,
    y: (item as BaseItem).y,
    rotation: isRotatable(item) ? (item as RotatableItem).rotation : 0,
    deltaX: (item as BaseItem).x - axe.x,
    deltaY: (item as BaseItem).y - axe.y
  }));
  
  await miro.board.bringToFront(axe);
  const deltaX = stickyNote.x - axe.x;
  const deltaY = stickyNote.y - axe.y;

  // Move to target
  for (const state of initialStates) {
    await updateItemPosition(state, state.x + deltaX, state.y + deltaY);
  }

  // Rotation animation
  const rad = Math.PI / 2;
  const cos = Math.cos(rad);
  const sin = Math.sin(rad);
  
  for (let i = 0; i < 3; i++) {
    for (const state of initialStates) {
      const rotatedX = stickyNote.x + (state.deltaX * cos - state.deltaY * sin);
      const rotatedY = stickyNote.y + (state.deltaX * sin + state.deltaY * cos);
      await updateItemPosition(state, rotatedX, rotatedY, state.rotation + 90);
    }
    await new Promise(resolve => setTimeout(resolve, 300));

    for (const state of initialStates) {
      await updateItemPosition(state, stickyNote.x + state.deltaX, stickyNote.y + state.deltaY, state.rotation);
    }
    await new Promise(resolve => setTimeout(resolve, 300));
  }

  onChopped();
  
  // Move back to original position
  for (const state of initialStates) {
    await updateItemPosition(state, state.x, state.y);
  }
}

const postInfoNotification = async (message: string) =>  
  await miro.board.notifications.showInfo(message);

const handleChoppingAction = async ({items}: {items:StickyNote[]}) => {
  const axe = await findExistingAxe();

  if (items.length === 0) {
    postInfoNotification("Couldn't find anything to chop 🤔")
    return;
  }

  if (!axe) {
    postInfoNotification('No axe found. Create it first.');
    return;
  }

  const target = items[0];
  playAxeAnimation({
    axe,
    stickyNote: target,
    onChopped: () => removeTree(target)
  });
};

const removeTree = async (root: StickyNote) => {
  const stickiesToRemove = await getStickiesToChopInTree(root, ["light_green", "red"]);
  
  for (const item of stickiesToRemove) {
    await miro.board.remove(item);
  }
};

export const findExistingAxe = async () => {
  const items = await miro.board.get({ type: 'image' });
  const axe = items.find(item => item.title === 'todo-tree-axe');
  return axe;
};

init();
