import type { FieldsInfo } from '@/types/model';
import { isType, type FieldType, type OneOf } from './kintone-rest-api-client';
import { getFieldObj } from './helper';

// conditionValue = '' | 'eq' | 'ne'
// conditionItem  = { value: 'eq', label: '=（等しい）', type: 'input', func: (a: string, b: string) => a === b }
//                = conditionMap[conditionValue]
export type ConditionValue = '' | '=' | '!=' | '>=' | '<=' | '<' | '>' | 'like' | 'not like' | 'in' | 'not in';

type ConditionItem = {
  value: ConditionValue;
  label: string | ((field: OneOf) => string);
  type: ComponentType | ((field: OneOf) => ComponentType);
};

export const conditionList: ConditionItem[] = [
  { value: '=', label: '=（等しい）', type: (field) => dateTimeComponentMap[field.type] || 'input' },
  { value: '!=', label: '≠ （等しくない）', type: (field) => dateTimeComponentMap[field.type] || 'input' },
  {
    value: '<=',
    label: (field) => (isDateTimeType(field) ? '≦ （以前）' : '≦ （以下）'),
    type: (field) => dateTimeComponentMap[field.type] || 'input',
  },
  { value: '<', label: '< （より前）', type: (field) => dateTimeComponentMap[field.type] || 'input' },
  {
    value: '>=',
    label: (field) => (isDateTimeType(field) ? '≧ （以降）' : '≧ （以上）'),
    type: (field) => dateTimeComponentMap[field.type] || 'input',
  },
  { value: '>', label: '> （より後）', type: (field) => dateTimeComponentMap[field.type] || 'input' },
  { value: 'like', label: '次のキーワードを含む', type: 'input' },
  { value: 'not like', label: '次のキーワードを含まない', type: 'input' },
  {
    value: 'in',
    label: (field) => (isMultiInputType(field) ? '次のいずれかと等しい' : '次のいずれかを含む'),
    type: (field) => multiValueComponentMap[field.type] || 'input',
  },
  {
    value: 'not in',
    label: (field) => (isMultiInputType(field) ? '次のいずれとも等しくない' : '次のいずれも含まない'),
    type: (field) => multiValueComponentMap[field.type] || 'input',
  },
];

// search from conditionList
// conditionItem = conditionMap[conditionValue]
export const conditionMap: Record<ConditionValue, ConditionItem> = conditionList.reduce(
  (map, item) => {
    map[item.value] = item;
    return map;
  },
  {} as Record<ConditionValue, ConditionItem>,
);

type FieldConditions = Partial<Record<FieldType, ConditionValue[]>>;
const textCondition: ConditionValue[] = ['=', '!=', 'in', 'like', 'not like'];
const numberCondition: ConditionValue[] = ['=', '!=', '<=', '>='];
const timeCondition: ConditionValue[] = ['=', '!=', '<=', '>=', '<', '>'];
const containsCondition: ConditionValue[] = ['in', 'not in'];

// FieldType -> ConditionValue[]
const fieldConditions: FieldConditions = {
  SINGLE_LINE_TEXT: textCondition,
  MULTI_LINE_TEXT: containsCondition,
  RICH_TEXT: containsCondition,
  NUMBER: numberCondition,
  CHECK_BOX: containsCondition,
  RADIO_BUTTON: containsCondition,
  DROP_DOWN: containsCondition,
  MULTI_SELECT: containsCondition,
  USER_SELECT: containsCondition,
  ORGANIZATION_SELECT: containsCondition,
  GROUP_SELECT: containsCondition,
  LINK: textCondition,
  CALC: numberCondition,

  TIME: timeCondition,
  DATE: timeCondition,
  DATETIME: timeCondition,
  CREATED_TIME: timeCondition,
  CREATOR: containsCondition,
  UPDATED_TIME: timeCondition,
  MODIFIER: containsCondition,
  RECORD_NUMBER: numberCondition,
} as const;

// fieldCode -> conditionList: ConditionItem[]
export const getAvailableCondition = (fieldCode: string, fieldsInfo: FieldsInfo, subTableCode: string | '') => {
  if (!fieldCode || !fieldsInfo.fields) return;
  const fieldObj = getFieldObj(fieldCode, fieldsInfo, '');
  if (!fieldObj) return;
  const conditions = fieldConditions[fieldObj.type] || textCondition; // TODO a fallback here
  return conditions.map((condition) => {
    const res = { ...conditionMap[condition] };
    res.label = typeof res.label === 'function' ? res.label(fieldObj) : res.label;
    return res;
  });
};

const dateTimeComponent = {
  time: 'kuc-time',
  date: 'date',
  datetime: 'datetime',
} as const;

export const multiValueComponent = {
  multiChoice: 'kuc-multichoice',
  multiInput: 'multi-input',
} as const;

const component = {
  input: 'kuc-text',
  select: 'kuc-combobox',
  ...dateTimeComponent,
  ...multiValueComponent,
} as const;

export type ComponentType = keyof typeof component;

const isDateTimeType = (field: OneOf) => {
  return field.type in dateTimeComponentMap;
};

const isMultiInputType = (field: OneOf) => {
  return multiValueComponentMap[field.type] === 'multiInput';
};

const dateTimeComponentMap: Partial<Record<FieldType, keyof typeof dateTimeComponent>> = {
  TIME: 'time',
  DATE: 'date',
  DATETIME: 'datetime',
  CREATED_TIME: 'datetime',
  UPDATED_TIME: 'datetime',
};

const multiValueComponentMap: Partial<Record<FieldType, keyof typeof multiValueComponent>> = {
  CHECK_BOX: 'multiChoice',
  DROP_DOWN: 'multiChoice',
  RADIO_BUTTON: 'multiChoice',
  MULTI_SELECT: 'multiChoice',
  SINGLE_LINE_TEXT: 'multiInput',
  LINK: 'multiInput',
};

export const getComponent = (value: ConditionValue, fieldObj: OneOf) => {
  if (!value || !fieldObj) return;
  const condition = conditionMap[value].type;
  return component[typeof condition === 'function' ? condition(fieldObj) : condition];
};

type DateFuncItem = {
  value: string;
  label: string | ((isTime: boolean) => string);
  condition?: 'datetime' | 'date';
  key: DateFuncKey;
};

export type DateFuncKey =
  | ''
  | 'FROM_TODAY'
  | '---NOW---'
  | 'NOW'
  | '---DAY---'
  | 'YESTERDAY'
  | 'TODAY'
  | 'TOMORROW'
  | '---WEEK---'
  | 'LAST_WEEK'
  | 'THIS_WEEK'
  | 'NEXT_WEEK'
  | '---MONTH---'
  | 'LAST_MONTH'
  | 'THIS_MONTH'
  | 'NEXT_MONTH'
  | '---YEAR---'
  | 'LAST_YEAR'
  | 'THIS_YEAR'
  | 'NEXT_YEAR';

export const dateFuncList: DateFuncItem[] = [
  { key: '', value: '%s', label: (isTime) => (isTime ? '日時を指定' : '日付を指定') },
  { key: 'FROM_TODAY', value: 'FROM_TODAY(%d, %s)', label: '今日から' },
  { key: '---NOW---', value: '\---NOW---', label: '' },
  { key: 'NOW', value: 'NOW()', label: '当時刻', condition: 'datetime' },
  { key: '---DAY---', value: '\---DAY---', label: '', condition: 'datetime' },
  { key: 'YESTERDAY', value: 'YESTERDAY()', label: '昨日' },
  { key: 'TODAY', value: 'TODAY()', label: '今日' },
  { key: 'TOMORROW', value: 'TOMORROW()', label: '明日' },
  { key: '---WEEK---', value: '\---WEEK---', label: '' },
  { key: 'LAST_WEEK', value: 'LAST_WEEK(%s)', label: '先週' },
  { key: 'THIS_WEEK', value: 'THIS_WEEK(%s)', label: '今週' },
  { key: 'NEXT_WEEK', value: 'NEXT_WEEK(%s)', label: '来週' },
  { key: '---MONTH---', value: '\---MONTH---', label: '' },
  { key: 'LAST_MONTH', value: 'LAST_MONTH(%s)', label: '先月' },
  { key: 'THIS_MONTH', value: 'THIS_MONTH(%s)', label: '今月' },
  { key: 'NEXT_MONTH', value: 'NEXT_MONTH(%s)', label: '来月' },
  { key: '---YEAR---', value: '\---YEAR---', label: '' },
  { key: 'LAST_YEAR', value: 'LAST_YEAR()', label: '昨年' },
  { key: 'THIS_YEAR', value: 'THIS_YEAR()', label: '今年' },
  { key: 'NEXT_YEAR', value: 'NEXT_YEAR()', label: '来年' },
];

// search from dateFuncList
// DateFuncItem = dateFuncMap[DateFuncKey]
export const dateFuncMap: Record<DateFuncKey, DateFuncItem> = dateFuncList.reduce(
  (map, item) => {
    map[item.key] = item;
    return map;
  },
  {} as Record<DateFuncKey, DateFuncItem>,
);

export const getDateFuncList = (hasTime: boolean) => {
  return dateFuncList.filter((item) => {
    return item.condition ? item.condition === (hasTime ? 'datetime' : 'date') : true;
  });
};
