import {
  type App,
  type PropType,
  type VNode,
  computed,
  defineComponent,
  ref,
  watchEffect
} from 'vue';
import dayjs, { Dayjs } from 'dayjs';
import isoWeek from 'dayjs/plugin/isoWeek';
import { _t } from '@kt/unity-locale';
import { getGuid } from '@kt/unity-utils';
import config from './config';
import style from './style/index.module.less';

// import { ElDatePicker } from 'element-plus';
import { ElDatePicker } from 'element-plus/es/components/date-picker/index';
// el-reset.css 重置 body、h1...、p，a 等浏览器原生标签
// import 'element-plus/theme-chalk/el-reset.css';
// 一个完整的 ElDatePicker 需要包含这些 css，参考 element-plus/es/components/date-picker/style/css.mjs
import 'element-plus/theme-chalk/base.css';
import 'element-plus/theme-chalk/el-date-picker.css';
import 'element-plus/theme-chalk/el-input.css';
import 'element-plus/theme-chalk/el-scrollbar.css';
import 'element-plus/theme-chalk/el-popper.css';
import 'element-plus/theme-chalk/el-button.css';

import {
  useAlignAntdStyle,
  useExtraFooter,
  useGetEls,
  useShortcutsPatch
} from './hooks';
import { d2D } from './utils';

// https://github.com/iamkun/dayjs/issues/2283
dayjs.extend(isoWeek);

const DEFAULT_FORMAT = 'YYYY-MM-DD HH:mm:ss';
const SIZE_MAP: Record<string, 'large' | 'default' | 'small'> = {
  large: 'large',
  middle: 'default',
  small: 'small'
};

const DateTimeRangePicker = defineComponent({
  name: 'DateTimeRangePicker',
  inheritAttrs: false,
  props: {
    value: {
      type: Object as PropType<[Dayjs, Dayjs]>
    },
    inputReadOnly: {
      type: Boolean,
      default: false
    },
    // 排除
    readOnly: null,
    allowClear: {
      type: Boolean,
      default: true
    },
    // 排除
    clearable: null,
    size: {
      type: String as PropType<'large' | 'middle' | 'small'>,
      default: 'middle'
    },
    placeholder: {
      type: [String, Array] as PropType<string | [string, string]>
    },
    startPlaceholder: null,
    endPlaceholder: null,
    valueFormat: {
      type: String
    },
    format: {
      type: String,
      default: DEFAULT_FORMAT
    },
    dropdownClassName: { type: String },
    popperClass: null,
    showTime: {
      type: [Boolean, Object] as PropType<
        | boolean
        | {
            defaultValue: [Dayjs, Dayjs] | null | undefined;
          }
      >,
      default: () => config.props.showTime
    },
    defaultTime: null,

    ranges: {
      type: [Boolean, Object] as PropType<
        | boolean
        | { [range: string]: Dayjs[] }
        | { [range: string]: () => Dayjs[] }
      >,
      default: () => config.props.ranges
    },
    shortcuts: null,

    disabledDate: {
      type: Function as PropType<(currentDate: Dayjs) => boolean>
    },

    // 业务需求的自定义扩展
    // 📝 antd 有提供了 props 和 插槽 对于页脚的两个处理
    renderExtraFooter: {
      type: Function as PropType<() => VNode>
    }
  },
  emits: ['update:value', 'calendar-change', 'change', 'ok', 'open-change'],
  setup(props, { attrs, emit, slots }) {
    const guid = getGuid();
    const myId = `el-date-editor-id--${guid}`;
    const myPopperId = `el-popper-id--${guid}`;
    const myCls = [myId, style['el-date-editor-extend']];
    const dropdownClassName = computed(() =>
      [myPopperId, props.dropdownClassName].join(' ')
    );

    const { elDateEditorEl, elPopperEl } = useGetEls(myId, myPopperId);
    // 底部额外插槽处理
    useExtraFooter(elPopperEl, { props, slots });
    // 快捷处理
    useShortcutsPatch(elPopperEl);
    // 样式对齐 antd
    useAlignAntdStyle(elDateEditorEl, elPopperEl, { props });

    const startPlaceholder = ref('');
    const endPlaceholder = ref('');
    watchEffect(() => {
      // 主动权交给外部
      if (props.placeholder) {
        let ph = props.placeholder;
        ph = Array.isArray(ph) ? ph : ph.slice(0, 2);
        startPlaceholder.value = props.placeholder[0];
        endPlaceholder.value = props.placeholder[1];
      } else {
        // 缺省处理
        startPlaceholder.value = _t('KTUnity.DateTimeRangePicker.startDate');
        endPlaceholder.value = _t('KTUnity.DateTimeRangePicker.endDate');
      }
    });

    const defaultTime = computed(() => {
      // 为了兼容之前写的 showTime ，这里判断如果 showTime 如果传入的是对象才会把主动权交给外部，否则内部处理缺省值
      if (typeof props.showTime === 'object') {
        // 如果外部不想要约束时间 00:00:00 - 23:59:59，直接传入 defaultValue null or undefined
        if (
          ([null, undefined] as unknown[]).includes(props.showTime.defaultValue)
        ) {
          return undefined;
        }
        return props.showTime.defaultValue;
      } else {
        return [dayjs('00:00:00', 'HH:mm:ss'), dayjs('23:59:59', 'HH:mm:ss')];
      }
    });

    const shortcuts = computed(() => {
      if (props.ranges === false) return undefined;

      // ant 规范转成 el 规范
      const ant2El = (
        data: { [range: string]: Dayjs[] } | { [range: string]: () => Dayjs[] }
      ) => {
        return Object.keys(data).map(key => ({
          text: key,
          value: data[key]
        }));
      };

      if (([true] as unknown[]).includes(props.ranges)) {
        // today: '今天',
        // yesterday: '昨天',
        // thisWeek: '本周',
        // lastWeek: '上周',
        // thisMonth: '本月',
        // lastMonth: '上月',
        return ant2El({
          [_t('KTUnity.DateTimeRangePicker.today')]: [
            dayjs().startOf('day'),
            dayjs().endOf('day')
          ],
          [_t('KTUnity.DateTimeRangePicker.yesterday')]: [
            dayjs().subtract(1, 'day').startOf('day'),
            dayjs().subtract(1, 'day').endOf('day')
          ],
          [_t('KTUnity.DateTimeRangePicker.thisWeek')]: [
            dayjs().startOf('isoWeek'),
            dayjs().endOf('isoWeek')
          ],
          [_t('KTUnity.DateTimeRangePicker.lastWeek')]: [
            dayjs().subtract(1, 'week').startOf('isoWeek'),
            dayjs().subtract(1, 'week').endOf('isoWeek')
          ],
          [_t('KTUnity.DateTimeRangePicker.thisMonth')]: [
            dayjs().startOf('month'),
            dayjs().endOf('month')
          ],
          [_t('KTUnity.DateTimeRangePicker.lastMonth')]: [
            dayjs().subtract(1, 'month').startOf('month'),
            dayjs().subtract(1, 'month').endOf('month')
          ]
        });
      }
      return ant2El(
        props.ranges as
          | { [range: string]: Dayjs[] }
          | { [range: string]: () => Dayjs[] }
      );
    });

    const disabledDate = (currentDate: Date) => {
      if (props.disabledDate) return !!props.disabledDate(dayjs(currentDate));
      return false;
    };

    // 清空时 value 为 null
    // ❌ 舍弃，使用 vModel 透传
    const handleUpdateModelValue = (value: [any, any] | null) => {
      let dates = value;
      if (value && value.length > 0) {
        dates = [
          d2D(value[0], props.valueFormat),
          d2D(value[1], props.valueFormat)
        ];
      }
      emit('update:value', dates);
    };

    const handleCalendarChange = (value: [any | null, any | null]) => {
      const dates = [
        d2D(value[0], props.valueFormat),
        d2D(value[1], props.valueFormat)
      ];
      const dateStrings = [
        d2D(value[0], props.format) ?? '',
        d2D(value[1], props.format) ?? ''
      ];
      emit('calendar-change', dates, dateStrings);
    };

    const handleChange = (value: [any, any] | null) => {
      if (value && value.length > 0) {
        // const dates = [
        //   d2D(value[0], props.valueFormat),
        //   d2D(value[1], props.valueFormat)
        // ];

        let dates = value;
        // 没有传入 value-format 就把 date 转成 dayjs，有传入就原样输出
        if (!props.valueFormat) {
          dates = [
            d2D(value[0], props.valueFormat),
            d2D(value[1], props.valueFormat)
          ];
        }

        // 📝 取消这个参数，没有意义，目前也无法计算出来（当有 value-format 场景时）
        // const dateStrings = [
        //   d2D(value[0], props.format) ?? '',
        //   d2D(value[1], props.format) ?? ''
        // ];
        // emit('change', dates, dateStrings);

        emit('change', dates);
        // 这里的 ok 意义不大，但是也保持上发
        emit('ok', dates);
      } else {
        // emit('change', null, ['', '']);
        emit('change', null);
        emit('ok', null);
      }
    };
    const handleVisibleChange = (val: boolean) => {
      emit('open-change', val);
    };

    // vModel 透传
    const vModel = computed({
      get: () => props.value,
      set: val => emit('update:value', val)
    });

    return () => (
      <ElDatePicker
        {...attrs}
        class={myCls}
        type={'datetimerange'}
        // model-value={props.value}
        // onUpdate:modelValue={handleUpdateModelValue}
        // ✅ 直接透传 vModel 因为 el 底层做了挺多处理的，保持其处理
        v-model={vModel.value}
        value-format={props.valueFormat}
        format={props.format}
        readonly={props.inputReadOnly}
        clearable={props.allowClear}
        size={SIZE_MAP[props.size]}
        start-placeholder={startPlaceholder.value}
        end-placeholder={endPlaceholder.value}
        popper-class={dropdownClassName.value}
        // TODO:el 存在 bug 后续跟踪，ElPopperOptions
        // popper-options={{ placement: 'bottom' }}
        range-separator={''}
        default-time={defaultTime.value}
        shortcuts={shortcuts.value}
        // 动态 bind，提升一定性能
        {...(props.disabledDate ? { disabledDate } : {})}
        // @ts-ignore
        onCalendarChange={handleCalendarChange}
        // @ts-ignore
        onChange={handleChange}
        // @ts-ignore
        onVisibleChange={handleVisibleChange}
      />
    );
  }
});

export default DateTimeRangePicker as typeof DateTimeRangePicker & {
  install: (app: App, ...options: any[]) => any;
};
