import { computed, ref, watch, type Ref } from 'vue';

import { useDebounceFn } from '@vueuse/core';

import { formatCellWithSuffix } from './cellDisplay';
import type { SpreadsheetColumnConfig } from '../types';

export interface FindMatch {
  row: number;
  col: number;
}

export function useSpreadsheetFind(options: {
  filteredRowIndices: Ref<number[]>;
  columns: Ref<SpreadsheetColumnConfig[]>;
  rows: Ref<Record<string, unknown>[]>;
  resolveDisplayValue: (
    viewRow: number,
    colCfg: SpreadsheetColumnConfig,
    row?: Record<string, unknown>,
  ) => unknown;
}) {
  const open = ref(false);
  const query = ref('');
  const matches = ref<FindMatch[]>([]);
  const activeIndex = ref(-1);

  function computeMatches(keyword: string): FindMatch[] {
    const q = keyword.trim().toLowerCase();
    if (!q) return [];

    const result: FindMatch[] = [];
    const cols = [...options.columns.value].sort((a, b) => a.col - b.col);
    const rowCount = options.filteredRowIndices.value.length;

    for (let viewRow = 0; viewRow < rowCount; viewRow += 1) {
      const dataRow = options.filteredRowIndices.value[viewRow];
      const row =
        dataRow !== undefined ? options.rows.value[dataRow] : undefined;
      if (!row) continue;

      for (const colCfg of cols) {
        const raw = options.resolveDisplayValue(viewRow, colCfg, row);
        const text = formatCellWithSuffix(raw, colCfg).toLowerCase();
        if (text.includes(q)) {
          result.push({ row: viewRow, col: colCfg.col });
        }
      }
    }
    return result;
  }

  const debouncedSearch = useDebounceFn((keyword: string) => {
    matches.value = computeMatches(keyword);
    activeIndex.value = matches.value.length > 0 ? 0 : -1;
  }, 150);

  watch(query, (v) => {
    if (!open.value) return;
    void debouncedSearch(v);
  });

  const matchCount = computed(() => matches.value.length);

  const activeMatch = computed(() => {
    if (activeIndex.value < 0 || activeIndex.value >= matches.value.length) {
      return null;
    }
    return matches.value[activeIndex.value] ?? null;
  });

  function openFind(initialQuery?: string) {
    open.value = true;
    if (initialQuery !== undefined) {
      query.value = initialQuery;
    }
    matches.value = computeMatches(query.value);
    activeIndex.value = matches.value.length > 0 ? 0 : -1;
  }

  function closeFind() {
    open.value = false;
    query.value = '';
    matches.value = [];
    activeIndex.value = -1;
  }

  function toggleFind() {
    if (open.value) closeFind();
    else openFind();
  }

  function goNext(): FindMatch | null {
    if (!matches.value.length) return null;
    activeIndex.value =
      activeIndex.value < 0
        ? 0
        : (activeIndex.value + 1) % matches.value.length;
    return matches.value[activeIndex.value] ?? null;
  }

  function goPrev(): FindMatch | null {
    if (!matches.value.length) return null;
    activeIndex.value =
      activeIndex.value < 0
        ? matches.value.length - 1
        : (activeIndex.value - 1 + matches.value.length) %
          matches.value.length;
    return matches.value[activeIndex.value] ?? null;
  }

  return {
    open,
    query,
    matches,
    activeIndex,
    matchCount,
    activeMatch,
    openFind,
    closeFind,
    toggleFind,
    goNext,
    goPrev,
  };
}
