<template>
  <div style="display: flex; position: relative">
    <kuc-combobox
      :value="funcValue"
      :items="options"
      @change.stop="updateFuncValue"
      :disabled="disabled"
      :className="shortConditionClass"
      :key="time"
    />

    <template v-if="isInput()">
      <kuc-datetime-picker v-if="time" :value="inputValue" @change.stop="updateValue" :disabled="disabled" />
      <kuc-date-picker v-else :value="inputValue" @change.stop="updateValue" :disabled="disabled" />
    </template>
    <kuc-combobox
      v-else-if="isWeek()"
      :items="weekOptions"
      :value="selectValue"
      @change.stop="updateValue"
      :disabled="disabled"
      :className="weekClassName"
    />
    <kuc-combobox
      v-else-if="isMonth()"
      :items="monthOptions"
      :value="selectValue"
      @change.stop="updateValue"
      :disabled="disabled"
      :className="monthClassName"
    />

    <template v-if="isFromToday()">
      <kuc-text
        :error="fromTodayError"
        :value="inputValue"
        @change.stop="updateFromTodayValue"
        :disabled="disabled"
        :className="fromTodayError ? 'from-today-input input error' : 'from-today-input input'"
      />
      <kuc-combobox
        :items="fromOptions"
        :value="selectValue"
        @change.stop="updateFromTodaySelectValue"
        :disabled="disabled"
        className="from-today-input"
      />
      <kuc-combobox
        :items="additionOptions"
        :value="additionSelectValue"
        @change.stop="updateFromTodayAdditionValue"
        :disabled="disabled"
        className="from-today-input addition"
      />
    </template>
  </div>
</template>

<script setup lang="ts">
import { dateFuncMap, getDateFuncList, type DateFuncKey } from '@/js/conditions';
import type { KucEvent } from '@/types/my-kintone';
import type { ComboboxChangeEventDetail } from 'kintone-ui-component';
import { defineProps, defineEmits, computed, ref, watch, inject, type Ref } from 'vue';

const props = defineProps<{
  value: string;
  time: boolean;
  disabled: boolean;
}>();

const canSave = inject<(canSave: boolean) => void>('canSave');

const inputValue = ref('');
const selectValue = ref('');
const additionSelectValue = ref('');
const funcValue = ref('');

const fromTodayError = ref('');

watch(
  () => fromTodayError.value,
  () => {
    canSave?.(!fromTodayError.value);
  },
);

const isInput = (func = funcValue.value) => func === dateFuncMap[''].value;
const isWeek = (func = funcValue.value) => func.includes('WEEK');
const isMonth = (func = funcValue.value) => func.includes('MONTH');
const isFromToday = (func = funcValue.value) => func.includes('FROM_TODAY');

const weekClassName = computed(() => {
  if (isWeek()) {
    return selectValue.value === DEFAULT_WEEK_MONTH ? 'week-all-combobox' : 'week-combobox';
  }
  return '';
});
const monthClassName = computed(() => {
  if (isMonth()) {
    return selectValue.value === DEFAULT_WEEK_MONTH ? 'month-all-combobox' : 'month-combobox';
  }
  return '';
});
const shortConditionClass = computed(() => {
  const className = 'datetime-condition-combobox';
  if (isInput()) { return className }
  if (isFromToday() || funcValue.value === dateFuncMap['NOW'].value) {
    return className + ' mid';
  } else {
    return className + ' short';
  }
});

const regex = /^(?<func>\w+)\((?<val>.*)\)$/;

watch(
  () => props.value,
  (current, before) => {
    if (props.value === '') {
      // select default one when empty
      funcValue.value = dateFuncMap[''].value;
      selectValue.value = '';
      inputValue.value = '';
      return;
    }

    const match = props.value.match(regex);

    funcValue.value = dateFuncMap[(match?.groups?.func || '') as DateFuncKey].value;

    const value = match?.groups?.val || (props.value.includes('%') ? '' : props.value);

    // TODO set values is this method but isFromToday
    if (isInput()) {
      inputValue.value = value;
      selectValue.value = '';
    } else if (isWeek() || isMonth()) {
      inputValue.value = '';
      selectValue.value = value || DEFAULT_WEEK_MONTH;
    } else if (isFromToday() && !(before && isFromToday(before))) {
      // only called when first open page
      const split = value.split(', ');
      inputValue.value = split[0] === '0' ? '' : split[0].replace('-', '');
      selectValue.value = split[1] || fromOptions[0].value;
      additionSelectValue.value = split[0] ? (split[0].startsWith('-') ? '-' : '+') : '+';
    }
  },
  { immediate: true },
);

const options = computed(() => {
  return getDateFuncList(props.time).map((item) => {
    return { label: typeof item.label === 'function' ? item.label(props.time) : item.label, value: item.value };
  });
});

const emit = defineEmits<{
  (e: 'change', data: KucEvent<{ value: string }>): void;
}>();

const updateValue = (event: KucEvent<ComboboxChangeEventDetail>) => {
  emit('change', { detail: { value: buildResult({ value: event.detail.value }) } });
};

const updateFuncValue = (event: KucEvent<ComboboxChangeEventDetail>) => {
  selectValue.value = '';
  inputValue.value = '';
  emit('change', { detail: { value: buildResult({ func: event.detail.value }) } });
};

const updateFromTodayValue = (event: KucEvent<ComboboxChangeEventDetail>) => {
  const value = buildFromTodayResult({ value: event.detail.value });
  if (!value) return;
  inputValue.value = event.detail.value as string;
  emit('change', { detail: { value } });
};

const updateFromTodaySelectValue = (event: KucEvent<ComboboxChangeEventDetail>) => {
  const value = buildFromTodayResult({ select: event.detail.value });
  if (!value) return;
  selectValue.value = event.detail.value as string;
  emit('change', { detail: { value } });
};

const updateFromTodayAdditionValue = (event: KucEvent<ComboboxChangeEventDetail>) => {
  const value = buildFromTodayResult({ addition: event.detail.value });
  if (!value) return;
  additionSelectValue.value = event.detail.value as string;
  emit('change', { detail: { value } });
};

type FromTodayParam = {
  value?: string;
  select?: string;
  addition?: string;
};

function buildFromTodayResult({
  value = inputValue.value || '0',
  select = selectValue.value || 'DAYS',
  addition = additionSelectValue.value || '-',
}: FromTodayParam) {
  if (value?.match(/^-?[0-9]+$/)) {
    fromTodayError.value = '';
    let res = value;
    if (value && value !== '0' && addition === '-') {
      if (value.startsWith('-')) {
        res = res.replace('-', '');
      } else {
        res = '-' + res;
      }
    }
    return funcValue.value.replace('%s', select).replace('%d', res);
  } else if (!fromTodayError.value) {
    inputValue.value = value || '';
    fromTodayError.value = '整数値を指定してください。';
    return;
  }
}

type Param = {
  func?: string;
  value?: string;
};

function buildResult({ func = funcValue.value, value }: Param) {
  let val = value || inputValue.value;
  if (isWeek(func) || isMonth(func)) {
    val = value || selectValue.value;
    val = val === DEFAULT_WEEK_MONTH ? '' : val;
  } else if (isFromToday(func)) {
    return func.replace('%d', val || '0').replace('%s', val || 'DAYS');
  }
  return func.replace('%s', val || '');
}

const DEFAULT_WEEK_MONTH = '_';

const fromOptions = [
  { label: '日', value: 'DAYS' },
  { label: '周', value: 'WEEKS' },
  { label: '月', value: 'MONTHS' },
  { label: '年', value: 'YEARS' },
];

const additionOptions = [
  { label: '前', value: '-' },
  { label: '後', value: '+' },
];

const weekOptions = [
  { label: 'すべての曜日', value: '_' },
  { label: '日', value: 'SUNDAY' },
  { label: '月', value: 'MONDAY' },
  { label: '火', value: 'TUESDAY' },
  { label: '水', value: 'WEDNESDAY' },
  { label: '木', value: 'THURSDAY' },
  { label: '金', value: 'FRIDAY' },
  { label: '土', value: 'SATURDAY' },
];

const monthOptions = [{ label: 'すべて', value: '_' }];
for (let i = 1; i <= 31; i++) {
  monthOptions.push({ label: i.toString() + '日', value: i.toString() });
}
monthOptions.push({ label: '末日', value: 'LAST' });
</script>
