// tslint:disable:object-literal-sort-keys
import 'jstree';
import $ from 'jquery';

const csrftoken = $('meta[name="csrf-token"]').attr('content');
const jstreeElement = $('#jstree');

// make clear_search callable globally (kept from original)
Window.clearFunction = jstreeElement.jstree(true).clear_search;

/**
 * --- Touch Long-Press to Enable Drag & Drop ---
 * Prevents accidental DnD on touchscreens while scrolling.
 * Dragging is enabled on touch only after a short hold (LONG_PRESS_MS).
 */
let allowTouchDrag = false;
let longPressTimer: number | null = null;
const LONG_PRESS_MS = 280;     // time to hold before DnD becomes active
const MOVE_CANCEL_PX = 8;      // movement threshold that cancels long-press (scroll intent)
let startX = 0;
let startY = 0;
let isRevertingMove = false;

// helper to detect touch-originating events
const isTouchEvent = (e: any) =>
  (e && (e.originalEvent?.type?.startsWith('touch') || e.type?.startsWith('touch')));

const cancelLongPress = () => {
  allowTouchDrag = false;
  if (longPressTimer) {
    clearTimeout(longPressTimer);
    longPressTimer = null;
  }
};

// set up long-press detection on tree anchors (before jsTree init)
jstreeElement.on('touchstart', '.jstree-anchor', function (e) {
  const t = e.originalEvent.touches?.[0];
  startX = t?.clientX ?? 0;
  startY = t?.clientY ?? 0;
  cancelLongPress();
  longPressTimer = window.setTimeout(() => {
    allowTouchDrag = true; // allow DnD after holding
  }, LONG_PRESS_MS);
});

jstreeElement.on('touchmove', function (e) {
  const t = e.originalEvent.touches?.[0];
  const dx = Math.abs((t?.clientX ?? 0) - startX);
  const dy = Math.abs((t?.clientY ?? 0) - startY);
  // if user starts scrolling or moves too much -> cancel long-press (no DnD)
  if (dx > MOVE_CANCEL_PX || dy > MOVE_CANCEL_PX) {
    cancelLongPress();
  }
});

jstreeElement.on('touchend touchcancel scroll', function () {
  cancelLongPress();
});

export default (menu) => {
  jstreeElement
    .jstree({
      conditionalselect: function (_node) {
        // keep original conditional select hook
        return window.myCustomOnChangeHandler();
      },
      core: {
        animation: 0,
        check_callback, // extracted below and improved
        data: {
          url(node) {
            return node.id === '#'
              ? '/TreeRoot'
              : '/TreeNodeConf/' + node.type + node.id;
          },
          dataType: 'json',
        },
      },
      // keep your provided types; check_callback will enforce rules live (see dnd.check_while_dragging)
      types: (window as any).menuTypes,
      contextmenu: { items: menu },
      search: {
        case_sensitive: false,
        show_only_matches: true,
        show_only_matches_children: true,
        ajax: {
          url: '/search',
        },
      },
      dnd: {
        // --- UX improvements & clarity ---
        check_while_dragging: true,   // evaluate check_callback live while dragging -> clearer allowed/forbidden states
        large_drop_target: true,      // easier to hit drop zones
        large_drag_target: true,      // easier to grab nodes
        open_timeout: 350,            // auto-open folders after hover delay
        inside_pos: 'last',           // default insert position when dropping "inside"

        // only allow DnD on touch after a long-press
        is_draggable: (_nodes, e) => {
          if (isTouchEvent(e)) return allowTouchDrag;
          return true; // mouse/pen: allow as usual
        },
      },
      plugins: [
        'conditionalselect',
        'contextmenu',
        'dnd',
        'json_data',
        'massload',
        'search',
        'state',
        'types',
        'wholerow',
      ],
    })
    .on('select_node.jstree', (e, data) => {
      onJsTreeSelect(e, data);
    })
    .on('show_contextmenu.jstree', () => {
      // sonst hinter Bootstrap Komponenten
      $('.jstree-contextmenu').css('z-index', '2000');
    })
    .on('dehover_node.jstree', () => {
      $('#tooltip').hide();
    })
    .on('move_node.jstree', onJsTreeNodeMove)
    .on('ready.jstree', (_evt, data) => {
      if (jstreeElement.find('li').html() === undefined) {
        // get that error)
        const Department = prompt(
          'Bitte neuen Bereichsnamen eingeben',
          'Bereich1',
        );
        if (!Department) {
          return;
        }
        if (Department) {
          $.post('/konfig/insertBer/' + Department, {
            _token: csrftoken,
          }).done((json) => {
            data = JSON.parse(json);
            jstreeElement.jstree().create_node(
              '#',
              { text: data.name, type: data.typ, id: 'BER' + data.id },
              'last',
              // tslint:disable-next-line:no-empty
              () => { },
            );
          });
        }
      }
    })
    .on('state_ready.jstree', function () {
      const idsToOpen: string[] = (window as any)['idsToOpen'];
      openPathInTree(idsToOpen);
    })
    .on('open_node.jstree', function (_e, _data) { })
    .on('search.jstree', function () {
      $('#jstree-config-searchicon').show();
      $('#jstree-config-searchingicon').hide();
    });

  $('#jstree-search').change(() => {
    const searchString = $('#jstree-search').val()?.toString().toLowerCase() ?? '';
    if (searchString.length === 0) {
      jstreeElement.jstree(true).clear_search();
    }
    let to = 0 as any;
    if (to) {
      clearTimeout(to);
    }
    to = setTimeout(() => {
      if (searchString.length < 3) {
        return;
      }
      $('#jstree-config-searchicon').hide();
      $('#jstree-config-searchingicon').show();
      jstreeElement.jstree(true).search(searchString);
    }, 250);
  });

  // Monkey patch functions onto jsTree instance
  jstreeElement.jstree(true).tasko = {
    getPathToAgb,
    openPathInTree,
  };

  return jstreeElement.jstree(true);
};

/**
 * --- Improved check_callback ---
 * Keeps your original move rules, but evaluates for both move & copy,
 * and runs during drag (via dnd.check_while_dragging) to give immediate feedback.
 */
function check_callback(
  operation: string,
  node: any,
  node_parent: any,
  _node_position?: any,
  _more?: any,
) {
  // allow all non-move/copy operations
  if (operation !== 'move_node' && operation !== 'copy_node') return true;

  // If moving/copying:
  // 1) node of type BER may only live directly under root
  if (node.type === 'BER') {
    if (node_parent?.id !== '#') return false;
  }

  // 2) BER cannot be parent of BER
  if (node_parent?.type === 'BER' && node.type === 'BER') {
    return false;
  }

  // 3) BER cannot be child of any non-root type
  if (node.type === 'BER' && node_parent?.type !== '#') {
    return false;
  }

  // 4) Any other types (not GRP and not BER) must have GRP parent
  if (node.type !== 'GRP' && node.type !== 'BER') {
    if (node_parent?.type !== 'GRP') return false;
  }

  return true;
}

const openPathInTree = (idsToOpen: string[], i: number = 0) => {
  const isAutoOpen = idsToOpen.length !== 0;
  if (i === 0 && isAutoOpen) {
    jstreeElement.jstree('close_all');
    jstreeElement.jstree('deselect_all', true);
  }

  if (i < idsToOpen.length) {
    const idToOpen = idsToOpen[i];
    jstreeElement.jstree('open_node', idToOpen, () => {
      openPathInTree(idsToOpen, i + 1);
    });
  }

  const lastNodeIndex = idsToOpen.length - 1;
  if (i === lastNodeIndex) {
    jstreeElement.jstree('select_node', idsToOpen[i]);
  }
};

const getPathToAgb = (agbId: string) => {
  return $.get(`/config/getPathToAgb/${agbId}`).then((res) => res.path);
};

const onJsTreeNodeMove = (e, data) => {
  if (isRevertingMove) {
    isRevertingMove = false;
    return;
  }

  const instance = data.instance;
  const sourceNode = data.node;
  const newParentId = data.parent;
  const oldParentId = data.old_parent;

  const newParentNode = instance.get_node(newParentId);
  const sourceText = sourceNode?.text ?? 'Unbekannt';
  const destText = newParentNode?.text ?? 'Unbekannt';

  const ok = window.confirm(`${sourceText} wird nach ${destText} verschoben. Bist du sicher?`);
  if (!ok) {
    isRevertingMove = true;
    instance.move_node(sourceNode, oldParentId, data.old_position);
    if (window?.toast?.error) window.toast.error('Verschieben abgebrochen');
    return;
  }

  const partype = instance.get_node(newParentId);

  if (partype.type === 'GRP' || partype.type === 'BER') {
    const Copynode = sourceNode.id;
    const id = newParentId;

    $.post('/konfig/insertAGB/Cut' + Copynode, {
      agb_parent: id,
      _token: csrftoken,
    }).done((resp) => {
      if (window?.toast) {
        (window as any).toast.success('Verschoben ' + resp);
      } else {
        alert('Verschoben ' + resp);
      }
    }).fail((_xhr) => {
      isRevertingMove = true;
      instance.move_node(sourceNode, oldParentId, data.old_position);
      if (window?.toast?.error) window.toast.error?.('Verschieben fehlgeschlagen – rückgängig gemacht');
      else alert('Verschieben fehlgeschlagen – rückgängig gemacht');
    });
  }
};


const onJsTreeSelect = (e, data) => {
  const ID = data?.node?.id ?? '';
  const TYPE = (data?.node?.type ?? '').substring(0, 3);
  const TEXT = data?.node?.text ?? '';
  window.history.pushState('setting', 'Config', `/config/${ID}`);
  window.currentConfigSelection = { id: ID, type: TYPE, text: TEXT };
  window.dispatchEvent(new CustomEvent('config:nodetype', {
    detail: window.currentConfigSelection
  }));

  switch (data.node.type.substring(0, 3)) {
    case 'LGC':
    case 'BSW':
    case 'MWT':
    case 'VBG':
    case 'WTG':
    case 'MLD':
    case 'STR':
    case 'BST':
    case 'BEM':
    case 'RWG':
    case 'INF':
    case 'DYN':
    case 'VBE':
    case 'MWE':
    case 'DOC':
    case 'MIP':
      $.get('/config/getagb/' + data.node.id, attachToView);
      break;
    case 'CST':
      $.get('/config/getcst/' + data.node.id, attachToView);
      break;
    case 'pdf':
    case 'html':
    case 'htm':
    case 'xls':
    case 'DEX':
    case 'REP':
    case 'LST':
    case 'mob':
    case 'MOD':
      //DDE-787
      $('#contend').load(`/config/getrep/${data.node.id}`, () => {
        if (typeof window.initializeTrennerStyles === 'function') {
          setTimeout(() => window.initializeTrennerStyles(), 150);
        }
      });
      break;
    case 'STT':
      $.get('/config/getstt/' + data.node.id, attachToView);
      break;
    case 'ORD':
      $.get('/config/getORD/' + data.node.id, attachToView);
      break;
    case 'GRP':
    case 'GRPi':
      //DDE-787
      $('#contend').load(`/config/getgrp/${data.node.id}`, () => {
        if (typeof window.initializeTrennerStyles === 'function') {
          setTimeout(() => window.initializeTrennerStyles(), 150);
        }
      });
      break;
    case 'BER':
      //DDE-787
      $('#contend').load(`/config/getber/${data.node.id}`, () => {
        if (typeof window.initializeTrennerStyles === 'function') {
          setTimeout(() => window.initializeTrennerStyles(), 150);
        }
      });
      break;
    case 'TRS':
      // $('#contend').load(`/config/getber/${data.node.id}`);
      break;
    case 'LGCx':
    case 'BSWx':
    case 'MWTx':
    case 'VBGx':
    case 'WTGx':
    case 'MLDx':
    case 'STRx':
    case 'BSTx':
    case 'BEMx':
    case 'RWGx':
    case 'INFx':
    case 'DYNx':
    case 'GRPx':
      // undelete placeholder
      break;
    default:
      alert('TYPe' + data.node.type);
  }
};

declare global {
  interface Window {
    clearFunction: () => void;
    myCustomOnChangeHandler: () => boolean;
    toast?: { success: (msg: string) => void; error?: (msg: string) => void };
    currentConfigSelection?: { id: string; type: string; text?: string };
    initializeTrennerStyles?: () => void;  // DDE-787
  }
}

window.clearFunction = function () {
  const jstreeInstance = jstreeElement.jstree(true);
  if (jstreeInstance && jstreeInstance.clear_search) {
    jstreeInstance.clear_search();
  }
};

const attachToView = (data) => {
  $('#contend').html(data);

  // DDE-787: Re-apply Trenner bold styling after AJAX content load
  if (typeof window.initializeTrennerStyles === 'function') {
    // Small delay to ensure DOM is fully rendered and selects are populated
    setTimeout(() => window.initializeTrennerStyles(), 150);
  }
};
