import {
  Key,
  matchesKey,
  truncateToWidth,
  type Component,
  type SelectItem,
  type SelectListTheme,
} from "@mariozechner/pi-tui";

// Local variant of pi-tui SelectList with configurable value/description column layout.

const normalizeToSingleLine = (text: string): string => text.replace(/[\r\n]+/g, " ").trim();

export interface SelectListColumnLayout {
  valueMaxWidth?: number;
  valueColumnWidth?: number;
  minDescriptionWidth?: number;
  minWidthForDescription?: number;
}

interface ResolvedSelectListColumnLayout {
  valueMaxWidth: number;
  valueColumnWidth: number;
  minDescriptionWidth: number;
  minWidthForDescription: number;
}

const DEFAULT_COLUMN_LAYOUT: ResolvedSelectListColumnLayout = {
  valueMaxWidth: 30,
  valueColumnWidth: 32,
  minDescriptionWidth: 10,
  minWidthForDescription: 40,
};

export class SelectListWithColumns implements Component {
  private items: SelectItem[] = [];
  private filteredItems: SelectItem[] = [];
  private selectedIndex = 0;
  private maxVisible = 5;
  private theme: SelectListTheme;
  private layout: ResolvedSelectListColumnLayout;

  public onSelect?: (item: SelectItem) => void;
  public onCancel?: () => void;
  public onSelectionChange?: (item: SelectItem) => void;

  constructor(
    items: SelectItem[],
    maxVisible: number,
    theme: SelectListTheme,
    layout: SelectListColumnLayout = {}
  ) {
    this.items = items;
    this.filteredItems = items;
    this.maxVisible = maxVisible;
    this.theme = theme;
    this.layout = {
      ...DEFAULT_COLUMN_LAYOUT,
      ...layout,
    };
  }

  setSelectedIndex(index: number): void {
    this.selectedIndex = Math.max(0, Math.min(index, this.filteredItems.length - 1));
  }

  invalidate(): void {
    // No cached state.
  }

  render(width: number): string[] {
    const lines: string[] = [];

    if (this.filteredItems.length === 0) {
      lines.push(this.theme.noMatch("  No matching commands"));
      return lines;
    }

    const startIndex = Math.max(
      0,
      Math.min(
        this.selectedIndex - Math.floor(this.maxVisible / 2),
        this.filteredItems.length - this.maxVisible
      )
    );
    const endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);

    for (let i = startIndex; i < endIndex; i++) {
      const item = this.filteredItems[i];
      if (!item) continue;

      const isSelected = i === this.selectedIndex;
      const descriptionSingleLine = item.description
        ? normalizeToSingleLine(item.description)
        : undefined;
      const displayValue = item.label || item.value;
      const prefix = isSelected ? "→ " : "  ";

      if (!descriptionSingleLine || width <= this.layout.minWidthForDescription) {
        lines.push(this.renderValueOnlyLine(prefix, displayValue, width, isSelected));
        continue;
      }

      const maxValueWidth = Math.min(this.layout.valueMaxWidth, width - prefix.length - 4);
      const truncatedValue = truncateToWidth(displayValue, maxValueWidth, "");
      const spacing = " ".repeat(Math.max(1, this.layout.valueColumnWidth - truncatedValue.length));
      const descriptionStart = prefix.length + truncatedValue.length + spacing.length;
      const descriptionWidth = width - descriptionStart - 2;

      if (descriptionWidth <= this.layout.minDescriptionWidth) {
        lines.push(this.renderValueOnlyLine(prefix, displayValue, width, isSelected));
        continue;
      }

      const truncatedDesc = truncateToWidth(descriptionSingleLine, descriptionWidth, "");
      if (isSelected) {
        lines.push(this.theme.selectedText(`${prefix}${truncatedValue}${spacing}${truncatedDesc}`));
      } else {
        lines.push(`${prefix}${truncatedValue}${this.theme.description(spacing + truncatedDesc)}`);
      }
    }

    if (startIndex > 0 || endIndex < this.filteredItems.length) {
      const scrollText = `  (${this.selectedIndex + 1}/${this.filteredItems.length})`;
      lines.push(this.theme.scrollInfo(truncateToWidth(scrollText, width - 2, "")));
    }

    return lines;
  }

  handleInput(keyData: string): void {
    if (matchesKey(keyData, Key.up)) {
      this.selectedIndex =
        this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;
      this.notifySelectionChange();
      return;
    }

    if (matchesKey(keyData, Key.down)) {
      this.selectedIndex =
        this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;
      this.notifySelectionChange();
      return;
    }

    if (matchesKey(keyData, Key.enter)) {
      const selectedItem = this.filteredItems[this.selectedIndex];
      if (selectedItem && this.onSelect) this.onSelect(selectedItem);
      return;
    }

    if (matchesKey(keyData, Key.escape) || keyData === "\u0003") {
      if (this.onCancel) this.onCancel();
    }
  }

  getSelectedItem(): SelectItem | null {
    const item = this.filteredItems[this.selectedIndex];
    return item || null;
  }

  private renderValueOnlyLine(
    prefix: string,
    displayValue: string,
    width: number,
    isSelected: boolean
  ): string {
    const maxWidth = width - prefix.length - 2;
    const line = `${prefix}${truncateToWidth(displayValue, maxWidth, "")}`;
    return isSelected ? this.theme.selectedText(line) : line;
  }

  private notifySelectionChange(): void {
    const selectedItem = this.filteredItems[this.selectedIndex];
    if (selectedItem && this.onSelectionChange) this.onSelectionChange(selectedItem);
  }
}
