import { handleListControlKey } from '../input/listController';
import { handleYesNoMenuInput, YES_NO_OPTIONS } from '../input/yesNoMenu';
import type { IncomingMessage, OutgoingMessage } from '../network/protocol';
import type { GameMode, SelectionContext, WorldItem } from '../state/gameState';

type ItemManagementAction = 'delete' | 'transfer';

export type ItemManagementOption = {
  action: ItemManagementAction;
  label: string;
  tooltip?: string;
};

export type ItemManagementConfirmContext = {
  itemId: string;
  action: ItemManagementAction;
  prompt: string;
  targetUserId?: string;
};

export type ItemTransferTarget = {
  userId: string;
  username: string;
  online: boolean;
};

type ItemControllerDeps = {
  state: {
    mode: GameMode;
    selectionContext: SelectionContext;
    selectedItemIds: string[];
    selectedItemIndex: number;
    selectedItemId: string | null;
    itemPropertyKeys: string[];
    itemPropertyIndex: number;
    editingPropertyKey: string | null;
    itemPropertyOptionValues: string[];
    itemPropertyOptionIndex: number;
    items: Map<string, WorldItem>;
    peers: Map<string, unknown>;
    player: { id: string | null };
  };
  signalingSend: (message: OutgoingMessage) => void;
  announceMenuEntry: (title: string, firstOption: string) => void;
  updateStatus: (message: string) => void;
  sfxUiBlip: () => void;
  sfxUiCancel: () => void;
  hasPermission: (key: string) => boolean;
  getAuthUserId: () => string;
  getItemManagementActionMetadata: (
    action: ItemManagementAction,
  ) =>
    | {
        label?: string;
        tooltip?: string;
        anyPermission?: string;
        ownPermission?: string;
      }
    | undefined;
  itemLabel: (item: WorldItem) => string;
  getEditableItemPropertyKeys: (item: WorldItem) => string[];
  getInspectItemPropertyKeys: (item: WorldItem) => string[];
  getItemPropertyValue: (item: WorldItem, key: string) => string;
  itemPropertyLabel: (key: string) => string;
  useItem: (item: WorldItem) => void;
  secondaryUseItem: (item: WorldItem) => void;
};

/**
 * Creates the shared item selection/management/property flow controller.
 */
export function createItemInteractionController(deps: ItemControllerDeps): {
  reset: () => void;
  beginItemSelection: (context: Exclude<SelectionContext, 'drop' | null>, items: WorldItem[]) => void;
  beginItemManagement: (item: WorldItem) => void;
  beginItemProperties: (item: WorldItem, showAll?: boolean) => void;
  recomputeActiveItemPropertyKeys: (itemId: string) => void;
  getManagementOptions: (item: WorldItem) => ItemManagementOption[];
  handleItemTransferTargets: (message: Extract<IncomingMessage, { type: 'item_transfer_targets' }>) => void;
  handleSelectItemModeInput: (code: string, key: string) => void;
  handleItemManageOptionsModeInput: (code: string, key: string) => void;
  handleItemManageTransferUserModeInput: (code: string, key: string) => void;
  handleConfirmYesNoModeInput: (code: string, key: string) => void;
} {
  let itemManagementSelectedItemId: string | null = null;
  let itemManagementOptions: ItemManagementOption[] = [];
  let itemManagementOptionIndex = 0;
  let itemManagementTargetUserIndex = 0;
  let itemManagementTransferTargets: ItemTransferTarget[] = [];
  let itemManagementConfirmIndex = 0;
  let itemManagementConfirmContext: ItemManagementConfirmContext | null = null;
  let itemPropertiesShowAll = false;

  function canManageDeleteItem(item: WorldItem): boolean {
    const metadata = deps.getItemManagementActionMetadata('delete');
    if (metadata?.anyPermission && deps.hasPermission(metadata.anyPermission)) return true;
    return Boolean(metadata?.ownPermission) && deps.hasPermission(metadata.ownPermission) && deps.getAuthUserId().length > 0 && item.createdBy === deps.getAuthUserId();
  }

  function canManageTransferItem(item: WorldItem): boolean {
    const metadata = deps.getItemManagementActionMetadata('transfer');
    if (metadata?.anyPermission && deps.hasPermission(metadata.anyPermission)) return true;
    return Boolean(metadata?.ownPermission) && deps.hasPermission(metadata.ownPermission) && deps.getAuthUserId().length > 0 && item.createdBy === deps.getAuthUserId();
  }

  function getManagementOptions(item: WorldItem): ItemManagementOption[] {
    const options: ItemManagementOption[] = [];
    const transferMetadata = deps.getItemManagementActionMetadata('transfer');
    if (canManageTransferItem(item) && (deps.state.player.id !== null || deps.state.peers.size > 0)) {
      options.push({
        action: 'transfer',
        label: transferMetadata?.label ?? 'Transfer item',
        tooltip: transferMetadata?.tooltip,
      });
    }
    const deleteMetadata = deps.getItemManagementActionMetadata('delete');
    if (canManageDeleteItem(item)) {
      options.push({
        action: 'delete',
        label: deleteMetadata?.label ?? 'Delete item',
        tooltip: deleteMetadata?.tooltip,
      });
    }
    return options;
  }

  function transferTargetLabel(target: ItemTransferTarget): string {
    return target.online ? `${target.username}, online` : `${target.username}, offline`;
  }

  function resetItemManagementState(): void {
    itemManagementSelectedItemId = null;
    itemManagementOptions = [];
    itemManagementOptionIndex = 0;
    itemManagementTransferTargets = [];
    itemManagementTargetUserIndex = 0;
    itemManagementConfirmIndex = 0;
    itemManagementConfirmContext = null;
  }

  function openItemManagementConfirm(context: ItemManagementConfirmContext): void {
    itemManagementConfirmContext = context;
    itemManagementConfirmIndex = 0;
    deps.state.mode = 'confirmYesNo';
    deps.announceMenuEntry(context.prompt, YES_NO_OPTIONS[itemManagementConfirmIndex].label);
  }

  function beginItemSelection(context: Exclude<SelectionContext, 'drop' | null>, items: WorldItem[]): void {
    if (items.length === 0) {
      deps.updateStatus('No items available.');
      deps.sfxUiCancel();
      return;
    }
    deps.state.mode = 'selectItem';
    deps.state.selectionContext = context;
    deps.state.selectedItemIds = items.map((item) => item.id);
    deps.state.selectedItemIndex = 0;
    deps.announceMenuEntry('Select item', deps.itemLabel(items[0]));
  }

  function beginItemManagement(item: WorldItem): void {
    const options = getManagementOptions(item);
    if (options.length === 0) {
      deps.updateStatus('No item management actions available.');
      deps.sfxUiCancel();
      return;
    }
    itemManagementSelectedItemId = item.id;
    itemManagementOptions = options;
    itemManagementOptionIndex = 0;
    deps.state.mode = 'itemManageOptions';
    deps.announceMenuEntry('Items', itemManagementOptions[0].label);
  }

  function beginItemProperties(item: WorldItem, showAll = false): void {
    itemPropertiesShowAll = showAll;
    deps.state.selectedItemId = item.id;
    deps.state.mode = 'itemProperties';
    deps.state.editingPropertyKey = null;
    deps.state.itemPropertyOptionValues = [];
    deps.state.itemPropertyOptionIndex = 0;
    deps.state.itemPropertyKeys = showAll ? deps.getInspectItemPropertyKeys(item) : deps.getEditableItemPropertyKeys(item);
    deps.state.itemPropertyIndex = 0;
    if (deps.state.itemPropertyKeys.length === 0) {
      deps.updateStatus('No properties available.');
      deps.sfxUiCancel();
      deps.state.mode = 'normal';
      deps.state.selectedItemId = null;
      return;
    }
    const key = deps.state.itemPropertyKeys[0];
    const value = deps.getItemPropertyValue(item, key);
    deps.updateStatus(`${deps.itemPropertyLabel(key)}: ${value}`);
    deps.sfxUiBlip();
  }

  function recomputeActiveItemPropertyKeys(itemId: string): void {
    if (deps.state.mode !== 'itemProperties' || deps.state.selectedItemId !== itemId) {
      return;
    }
    const item = deps.state.items.get(itemId);
    if (!item) {
      return;
    }
    const previousKey = deps.state.itemPropertyKeys[deps.state.itemPropertyIndex] ?? null;
    const nextKeys = itemPropertiesShowAll ? deps.getInspectItemPropertyKeys(item) : deps.getEditableItemPropertyKeys(item);
    deps.state.itemPropertyKeys = nextKeys;
    if (nextKeys.length === 0) {
      deps.state.itemPropertyIndex = 0;
      return;
    }
    if (previousKey && nextKeys.includes(previousKey)) {
      deps.state.itemPropertyIndex = nextKeys.indexOf(previousKey);
      return;
    }
    deps.state.itemPropertyIndex = Math.max(0, Math.min(deps.state.itemPropertyIndex, nextKeys.length - 1));
  }

  function handleItemTransferTargets(message: Extract<IncomingMessage, { type: 'item_transfer_targets' }>): void {
    if (itemManagementSelectedItemId !== message.itemId) return;
    itemManagementTransferTargets = [...message.targets].sort((a, b) =>
      a.username.localeCompare(b.username, undefined, { sensitivity: 'base' }),
    );
    if (itemManagementTransferTargets.length === 0) {
      deps.state.mode = 'itemManageOptions';
      deps.updateStatus('No users available to transfer to.');
      deps.sfxUiCancel();
      return;
    }
    itemManagementTargetUserIndex = 0;
    deps.state.mode = 'itemManageTransferUser';
    deps.announceMenuEntry('Users', transferTargetLabel(itemManagementTransferTargets[0]));
  }

  function handleSelectItemModeInput(code: string, key: string): void {
    if (deps.state.selectedItemIds.length === 0) {
      deps.state.mode = 'normal';
      deps.state.selectionContext = null;
      return;
    }
    const control = handleListControlKey(code, key, deps.state.selectedItemIds, deps.state.selectedItemIndex, (itemId) => {
      const item = deps.state.items.get(itemId);
      return item ? deps.itemLabel(item) : '';
    });
    if (control.type === 'move') {
      deps.state.selectedItemIndex = control.index;
      const current = deps.state.items.get(deps.state.selectedItemIds[deps.state.selectedItemIndex]);
      if (current) {
        deps.updateStatus(deps.itemLabel(current));
        deps.sfxUiBlip();
      }
      return;
    }
    if (control.type === 'select') {
      const selected = deps.state.items.get(deps.state.selectedItemIds[deps.state.selectedItemIndex]);
      if (!selected) {
        deps.state.mode = 'normal';
        deps.state.selectionContext = null;
        return;
      }
      const context = deps.state.selectionContext;
      deps.state.mode = 'normal';
      deps.state.selectionContext = null;
      if (context === 'pickup') {
        deps.signalingSend({ type: 'item_pickup', itemId: selected.id });
        return;
      }
      if (context === 'delete') {
        deps.signalingSend({ type: 'item_delete', itemId: selected.id });
        return;
      }
      if (context === 'edit') {
        beginItemProperties(selected);
        return;
      }
      if (context === 'use') {
        deps.useItem(selected);
        return;
      }
      if (context === 'secondaryUse') {
        deps.secondaryUseItem(selected);
        return;
      }
      if (context === 'inspect') {
        beginItemProperties(selected, true);
        return;
      }
      if (context === 'manage') {
        beginItemManagement(selected);
      }
      return;
    }
    if (control.type === 'cancel') {
      deps.state.mode = 'normal';
      deps.state.selectionContext = null;
      deps.updateStatus('Cancelled.');
      deps.sfxUiCancel();
    }
  }

  function handleItemManageOptionsModeInput(code: string, key: string): void {
    if (!itemManagementSelectedItemId) {
      deps.state.mode = 'normal';
      resetItemManagementState();
      return;
    }
    const item = deps.state.items.get(itemManagementSelectedItemId);
    if (!item) {
      deps.state.mode = 'normal';
      resetItemManagementState();
      deps.updateStatus('Item no longer exists.');
      deps.sfxUiCancel();
      return;
    }
    itemManagementOptions = getManagementOptions(item);
    if (itemManagementOptions.length === 0) {
      deps.state.mode = 'normal';
      resetItemManagementState();
      deps.updateStatus('No item management actions available.');
      deps.sfxUiCancel();
      return;
    }
    itemManagementOptionIndex = Math.max(0, Math.min(itemManagementOptionIndex, itemManagementOptions.length - 1));
    const control = handleListControlKey(code, key, itemManagementOptions, itemManagementOptionIndex, (entry) => entry.label);
    if (control.type === 'move') {
      itemManagementOptionIndex = control.index;
      deps.updateStatus(itemManagementOptions[itemManagementOptionIndex].label);
      deps.sfxUiBlip();
      return;
    }
    if (code === 'Space') {
      deps.updateStatus(itemManagementOptions[itemManagementOptionIndex]?.tooltip ?? 'No tooltip available.');
      deps.sfxUiBlip();
      return;
    }
    if (control.type === 'select') {
      const option = itemManagementOptions[itemManagementOptionIndex];
      if (option.action === 'delete') {
        openItemManagementConfirm({
          itemId: item.id,
          action: 'delete',
          prompt: `Delete ${deps.itemLabel(item)}?`,
        });
        return;
      }
      itemManagementTransferTargets = [];
      itemManagementTargetUserIndex = 0;
      deps.signalingSend({ type: 'item_transfer_targets', itemId: item.id });
      deps.updateStatus('Loading users...');
      deps.sfxUiBlip();
      return;
    }
    if (control.type === 'cancel') {
      deps.state.mode = 'normal';
      resetItemManagementState();
      deps.updateStatus('Cancelled.');
      deps.sfxUiCancel();
    }
  }

  function handleItemManageTransferUserModeInput(code: string, key: string): void {
    if (!itemManagementSelectedItemId || itemManagementTransferTargets.length === 0) {
      deps.state.mode = 'itemManageOptions';
      return;
    }
    const control = handleListControlKey(
      code,
      key,
      itemManagementTransferTargets,
      itemManagementTargetUserIndex,
      (target) => transferTargetLabel(target),
    );
    if (control.type === 'move') {
      itemManagementTargetUserIndex = control.index;
      const label = transferTargetLabel(itemManagementTransferTargets[itemManagementTargetUserIndex]);
      deps.updateStatus(label);
      deps.sfxUiBlip();
      return;
    }
    if (control.type === 'select') {
      const item = deps.state.items.get(itemManagementSelectedItemId);
      const target = itemManagementTransferTargets[itemManagementTargetUserIndex];
      if (!item || !target) {
        deps.state.mode = 'itemManageOptions';
        deps.sfxUiCancel();
        return;
      }
      openItemManagementConfirm({
        itemId: item.id,
        action: 'transfer',
        prompt: `Transfer ${deps.itemLabel(item)} to ${target.username}?`,
        targetUserId: target.userId,
      });
      return;
    }
    if (control.type === 'cancel') {
      deps.state.mode = 'itemManageOptions';
      deps.updateStatus(itemManagementOptions[itemManagementOptionIndex]?.label ?? 'Item management.');
      deps.sfxUiCancel();
    }
  }

  function handleConfirmYesNoModeInput(code: string, key: string): void {
    if (!itemManagementConfirmContext) {
      deps.state.mode = 'normal';
      resetItemManagementState();
      return;
    }
    const control = handleYesNoMenuInput(code, key, itemManagementConfirmIndex);
    if (control.type === 'move') {
      itemManagementConfirmIndex = control.index;
      deps.updateStatus(YES_NO_OPTIONS[itemManagementConfirmIndex].label);
      deps.sfxUiBlip();
      return;
    }
    if (control.type === 'cancel') {
      deps.state.mode = 'itemManageOptions';
      itemManagementConfirmContext = null;
      deps.updateStatus(itemManagementOptions[itemManagementOptionIndex]?.label ?? 'Item management.');
      deps.sfxUiCancel();
      return;
    }
    if (control.type === 'select') {
      const selected = YES_NO_OPTIONS[itemManagementConfirmIndex];
      const context = itemManagementConfirmContext;
      itemManagementConfirmContext = null;
      if (selected.id === 'no') {
        deps.state.mode = 'itemManageOptions';
        deps.updateStatus(itemManagementOptions[itemManagementOptionIndex]?.label ?? 'Cancelled.');
        deps.sfxUiCancel();
        return;
      }
      deps.state.mode = 'normal';
      if (context.action === 'delete') {
        deps.signalingSend({ type: 'item_delete', itemId: context.itemId });
      } else if (context.action === 'transfer' && context.targetUserId) {
        deps.signalingSend({ type: 'item_transfer', itemId: context.itemId, targetUserId: context.targetUserId });
      }
      resetItemManagementState();
    }
  }

  return {
    reset: resetItemManagementState,
    beginItemSelection,
    beginItemManagement,
    beginItemProperties,
    recomputeActiveItemPropertyKeys,
    getManagementOptions,
    handleItemTransferTargets,
    handleSelectItemModeInput,
    handleItemManageOptionsModeInput,
    handleItemManageTransferUserModeInput,
    handleConfirmYesNoModeInput,
  };
}
