<script lang="ts" setup>
import { computed, nextTick, ref, watch } from 'vue';

import { Button, Input, Popover } from 'ant-design-vue';

import type { SpreadsheetFormulaItem } from '#/api/spreadsheet-formula';

const props = withDefaults(
  defineProps<{
    cellAddress: string;
    sourceValue: string;
    editable: boolean;
    formulas: SpreadsheetFormulaItem[];
    isDark?: boolean;
    referenceMode?: boolean;
    caretPosition?: number;
  }>(),
  {
    isDark: false,
    referenceMode: false,
    caretPosition: 0,
  },
);

const emit = defineEmits<{
  commit: [value: string];
  'draft-change': [value: string];
  'caret-change': [position: number];
}>();

const draft = ref('');
const fxOpen = ref(false);
const focused = ref(false);
const inputRef = ref<{ input: HTMLInputElement } | null>(null);

function syncDraftFromProps() {
  if (focused.value) return;
  draft.value = props.sourceValue;
}

watch(
  () => props.cellAddress,
  () => syncDraftFromProps(),
  { immediate: true },
);

watch(
  () => props.sourceValue,
  () => syncDraftFromProps(),
);

watch(
  () => props.caretPosition,
  async (pos) => {
    if (!focused.value || pos == null) return;
    await nextTick();
    const el = inputRef.value?.input;
    if (el) {
      el.setSelectionRange(pos, pos);
    }
  },
);

function syncCaret() {
  const el = inputRef.value?.input;
  if (!el) return;
  emit('caret-change', el.selectionStart ?? draft.value.length);
}

function onFocus() {
  focused.value = true;
  syncCaret();
}

function onBlur() {
  focused.value = false;
  if (!props.editable || props.referenceMode) return;
  emit('commit', draft.value);
}

function onEnter() {
  focused.value = false;
  if (!props.editable) return;
  emit('commit', draft.value);
}

function onInput() {
  emit('draft-change', draft.value);
  syncCaret();
}

function insertFormula(item: SpreadsheetFormulaItem) {
  draft.value = item.expression_template.replace(/REF(\d+)/g, (_, n) => {
    return `${String.fromCharCode(65 + Number(n))}1`;
  });
  emit('draft-change', draft.value);
  fxOpen.value = false;
}

function insertAtCaret(text: string) {
  const pos = props.caretPosition ?? draft.value.length;
  draft.value = draft.value.slice(0, pos) + text + draft.value.slice(pos);
  const nextCaret = pos + text.length;
  emit('draft-change', draft.value);
  emit('caret-change', nextCaret);
  void nextTick(() => {
    const el = inputRef.value?.input;
    if (el) {
      el.focus();
      el.setSelectionRange(nextCaret, nextCaret);
    }
  });
}

function focusInput() {
  inputRef.value?.input?.focus();
}

const formulaList = computed(() => props.formulas);

defineExpose({
  insertAtCaret,
  focusInput,
  getDraft: () => draft.value,
  setDraft: (v: string) => {
    draft.value = v;
    emit('draft-change', v);
  },
});
</script>

<template>
  <div
    class="spreadsheet-formula-bar"
    :class="{ 'is-dark': isDark }"
  >
    <div class="cell-address">{{ cellAddress || ' ' }}</div>
    <Popover
      v-model:open="fxOpen"
      placement="bottomLeft"
      trigger="click"
      :overlay-class-name="
        isDark ? 'sheet-fx-popover is-dark' : 'sheet-fx-popover'
      "
    >
      <template #content>
        <div class="fx-list" :class="{ 'is-dark': isDark }">
          <div
            v-for="item in formulaList"
            :key="item.id"
            class="fx-item"
            @click="insertFormula(item)"
          >
            <strong>{{ item.name }}</strong>
            <span>{{ item.expression_template }}</span>
            <small v-if="item.description">{{ item.description }}</small>
          </div>
          <div v-if="!formulaList.length" class="fx-empty">暂无公式</div>
        </div>
      </template>
      <Button class="fx-btn" size="small" type="text">fx</Button>
    </Popover>
    <Input
      ref="inputRef"
      v-model:value="draft"
      class="formula-input"
      :readonly="!editable"
      @blur="onBlur"
      @click="syncCaret"
      @focus="onFocus"
      @input="onInput"
      @keydown.enter.prevent="onEnter"
      @keyup="syncCaret"
    />
  </div>
</template>

<style scoped lang="scss">
.spreadsheet-formula-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-top: 1px solid #e5e6eb;
  background: #fafafa;

  &.is-dark {
    background: #1f1f1f;
    border-top-color: #424242;

    .cell-address {
      background: #141414;
      border-color: #424242;
      color: rgba(255, 255, 255, 0.85);
    }

    .fx-btn {
      color: #3c89e8;
    }
  }
}

.cell-address {
  min-width: 48px;
  height: 28px;
  line-height: 28px;
  padding: 0 8px;
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  border: 1px solid #e5e6eb;
  border-radius: 4px;
  background: #fff;
}

.fx-btn {
  font-style: italic;
  font-weight: 600;
  color: #1668dc;
}

.formula-input {
  flex: 1;
}

.fx-list {
  width: 320px;
  max-height: 280px;
  overflow: auto;
}

.fx-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px;
  cursor: pointer;
  border-radius: 4px;

  &:hover {
    background: #f2f3f5;
  }

  span {
    font-size: 12px;
    color: #4e5969;
    font-family: monospace;
  }

  small {
    font-size: 11px;
    color: #86909c;
  }
}

.fx-list.is-dark {
  .fx-item {
    color: rgba(255, 255, 255, 0.85);

    &:hover {
      background: #303030;
    }

    span {
      color: rgba(255, 255, 255, 0.65);
    }

    small {
      color: rgba(255, 255, 255, 0.45);
    }
  }

  .fx-empty {
    color: rgba(255, 255, 255, 0.45);
  }
}

.fx-empty {
  padding: 12px;
  color: #86909c;
  text-align: center;
}
</style>

<style lang="scss">
.sheet-fx-popover.is-dark {
  .ant-popover-inner {
    background: #1f1f1f;
    border: 1px solid #424242;
  }
}
</style>
