import {
  App,
  computed,
  defineComponent,
  nextTick,
  onMounted,
  PropType,
  ref,
  watch
} from 'vue';
import { RangePicker } from 'ant-design-vue/es/date-picker';
import { TimePicker } from 'ant-design-vue/es/time-picker';
import Button from 'ant-design-vue/es/button';
import { SwapRightOutlined } from '@ant-design/icons-vue';
import 'ant-design-vue/es/date-picker/style';
import 'ant-design-vue/es/time-picker/style';
import 'ant-design-vue/es/button/style';

// @ts-ignore
import vClickOutside from 'v-click-outside';
// @ts-ignore
import style from './style/index.module.less';

import dayjs, { Dayjs } from 'dayjs';
import { getGuid, mountComponent, getDomPolyfill } from '@kt/unity-utils';
import { debounce } from 'lodash-es';

const getParent = (element: any, className: string, max = 10) => {
  let p = null,
    n = 0;
  const run = (element: any, className: string) => {
    // 第一个就找到，返回本身，有些元素 没有 className，这里就会返回 undefined 导致判断错误，这里默认赋值 -1
    if ((element?.className?.indexOf?.(className) ?? -1) !== -1) {
      p = element;
      return;
    }
    if (n++ >= max) return;

    const pClsList = element?.parentNode?.className;
    const pNode = element?.parentNode;
    if ((pClsList?.indexOf?.(className) ?? -1) === -1) {
      run(pNode, className);
    } else {
      p = pNode;
    }
  };
  run(element, className);
  return p;
};

const DateTimeRangePicker = defineComponent({
  name: 'DateTimeRangePicker',
  props: {
    value: {
      type: Object as PropType<[Dayjs, Dayjs]>
    },
    format: {
      type: String,
      default: 'YYYY-MM-DD HH:mm:ss'
    },
    // 不开放这些 props 到外部
    open: {},
    showTime: {},
    dropdownClassName: {}
  },
  emits: ['update:value', 'change'],
  setup(props, { attrs, emit }) {
    const openRangePicker = ref(false);
    const value = ref<[Dayjs, Dayjs]>();

    const guid = getGuid();
    const clsDateTimeRangePicker = `date-time-range-picker-${guid}`;
    const clsDateTimeRangePickerDropdown = `date-time-range-picker-dropdown-${guid}`;

    /**
     * 设置一个日期的时间部分
     */
    const setTimePart = (date: Dayjs, time: Dayjs) => {
      const hour = time.get('hour');
      const minute = time.get('minute');
      const second = time.get('second');
      return date.set('hour', hour).set('minute', minute).set('second', second);
    };

    /**
     * 选择时间的时候出发
     */
    const handleCalendarChange = (
      dates: [Dayjs, Dayjs]
      // dateStrings: [string, string],
      // { range }: { range: 'start' | 'end' }
    ) => {
      // console.log('handleCalendarChange-dates :>> ', dates);
      // 监听当前是否被清空
      if (dates === null) {
        // 赋值为空
        value.value = undefined;

        // 顺便清空底部 timepicker
        vlaueTimePicker1.value = undefined;
        vlaueTimePicker2.value = undefined;
        openRangePicker.value = false;
        return;
      }

      if (dates[0]) {
        if (!vlaueTimePicker1.value) {
          dates[0] = dates[0]?.startOf('day');
        } else {
          dates[0] = setTimePart(dates[0], vlaueTimePicker1.value);
        }
      }
      if (dates[1]) {
        if (!vlaueTimePicker2.value) {
          dates[1] = dates[1]?.endOf('day');
        } else {
          dates[1] = setTimePart(dates[1], vlaueTimePicker2.value);
        }
      }

      if (dates.filter(Boolean).length === 2) {
        value.value = dates;
        // 初始化 time2 time2
        vlaueTimePicker1.value =
          vlaueTimePicker1.value || dayjs('00:00:00', 'HH:mm:ss');
        vlaueTimePicker2.value =
          vlaueTimePicker2.value || dayjs('23:59:59', 'HH:mm:ss');
      }
    };

    /**
     * 弹出面板显隐藏时触发
     */
    const handleOpenChange = (status: boolean) => {
      // 当前已经选择两个时间且面板是开着的状态，就不要关闭
      if (value.value?.filter(Boolean).length === 2 && openRangePicker.value)
        return;
      openRangePicker.value = status;
      if (!status) {
        openTimePicker1.value = openTimePicker2.value = false;
      }
    };

    let rangePickerDropdownEL: HTMLElement | null = null;
    /**
     * rangePicker 注册相关额外事件
     */
    const { bind, unbind } = vClickOutside.directive;
    watch(openRangePicker, async (val: boolean) => {
      // 只注册一次
      if (val && !rangePickerDropdownEL) {
        await nextTick();

        // vClickOutside 控制
        rangePickerDropdownEL = getDomPolyfill(
          `.${clsDateTimeRangePickerDropdown}`
        );

        // 存在时才进行 bind
        if (rangePickerDropdownEL) {
          bind(rangePickerDropdownEL, {
            value: {
              handler(e: any) {
                if (openRangePicker.value) {
                  openRangePicker.value = false;
                }
              },
              middleware(e: any) {
                if (openRangePicker.value) {
                  const res = getParent(e.target, clsDateTimeRangePicker, 5);
                  return !res;
                }
                return false;
              }
            }
          });
        }

        // 底部 range 事件委托监听
        const antPickerRangesEL =
          rangePickerDropdownEL?.querySelector('.ant-picker-ranges');
        antPickerRangesEL?.addEventListener('click', (e: any) => {
          if (e.target.className.indexOf('ant-tag') !== -1) {
            value.value = [
              dayjs(value.value?.[0]?.startOf('day')),
              dayjs(value.value?.[1]?.endOf('day'))
            ];
            // 把时间范围格式化成 00:00:00 至 23:59:59
            // 这里会 range-picker change 较后面触发，所以会导致 watch newValueStr 触发两次，使用 debounce 来控制
            vlaueTimePicker1.value = dayjs('00:00:00', 'HH:mm:ss');
            vlaueTimePicker2.value = dayjs('23:59:59', 'HH:mm:ss');

            // 关闭
            openRangePicker.value = false;
          }
        });

        // 是否需要动态创建确定按钮，当 attrs.ranges 有值时
        if (attrs.ranges) {
          createButtonInstance(rangePickerDropdownEL!);
        }
      }
    });

    // #region 监听日期改变，用于 emit
    const newValueStr = computed(() => {
      if (value.value?.filter(Boolean).length === 2) {
        return [
          dayjs(value.value[0]).format('YYYY-MM-DD HH:mm:ss'),
          dayjs(value.value[1]).format('YYYY-MM-DD HH:mm:ss')
        ].join('_');
      }
      return '';
    });

    // debounce 防止 change 触发多次
    const runEmit = debounce((val: string) => {
      // console.log('✨ date-time-range-picker-change...');
      emit('update:value', val === '' ? null : value.value);
      const dateStrings = [
        value.value?.[0]?.format(props.format) || '',
        value.value?.[1]?.format(props.format) || ''
      ];
      emit('change', value.value, dateStrings);
    }, 10);
    watch(newValueStr, (val: string) => {
      runEmit(val);
    });
    // #endregion

    // #region timePicker
    const disabledTimePicker = computed(() => {
      return !(value.value?.filter(Boolean).length === 2);
    });
    const getPopupContainer = (trigger: any) => {
      return trigger.parentNode;
    };
    const vlaueTimePicker1 = ref<Dayjs>();
    const openTimePicker1 = ref(false);
    const handleChangeTime1 = () => {
      if (vlaueTimePicker1.value === null)
        vlaueTimePicker1.value = dayjs('00:00:00', 'HH:mm:ss');
      value.value![0] = setTimePart(value.value![0], vlaueTimePicker1.value!);
    };
    const vlaueTimePicker2 = ref<Dayjs>();
    const openTimePicker2 = ref(false);
    const handleChangeTime2 = () => {
      if (vlaueTimePicker2.value === null)
        vlaueTimePicker2.value = dayjs('23:59:59', 'HH:mm:ss');
      value.value![1] = setTimePart(value.value![1], vlaueTimePicker2.value!);
    };
    // #endregion

    const handleConfirm = () => {
      openRangePicker.value = false;
    };

    // ✨ 创建一个可动态 append 到 ranges dom 后面的按钮
    const createButtonInstance = (node: HTMLElement) => {
      const { instance, unmount } = mountComponent(
        {
          setup() {
            const handleConfirm = () => {
              openRangePicker.value = false;
            };
            return () => (
              <Button type="primary" size="small" onClick={handleConfirm}>
                确定
              </Button>
            );
          }
        },

        // extra
        {
          parent: node,
          createRoot() {
            // 参考 antd 配置了 ranges 按钮的 dom 结构
            const root = document.createElement('li');
            root.className = 'ant-picker-ok';
            return root;
          },
          insert(target) {
            const $antPickerFooterEL = node.querySelector('.ant-picker-ranges');
            $antPickerFooterEL?.appendChild(target);
          },
          remove(target) {
            node.querySelector('.ant-picker-ranges')?.removeChild(target);
          }
        }
      );
      return instance;
    };

    onMounted(() => {
      if (props.value?.filter(Boolean).length === 2) {
        value.value = props.value!;
        // 初始化底部时间
        vlaueTimePicker1.value = dayjs(
          value.value[0].format('HH:mm:ss'),
          'HH:mm:ss'
        );
        vlaueTimePicker2.value = dayjs(
          value.value[1].format('HH:mm:ss'),
          'HH:mm:ss'
        );
      }
    });
    const slots = {
      renderExtraFooter: () => (
        <>
          <div
            class={`flex items-center -mx-[12px] ${
              !attrs.ranges
                ? 'border-0 border-b border-solid border-[#f0f0f0]'
                : ''
            }`}
          >
            <TimePicker
              class="flex-1"
              popupClassName="date-time-range-picker-time-picker"
              placeholder="开始时间"
              getPopupContainer={getPopupContainer}
              v-model:value={vlaueTimePicker1.value}
              v-model:open={openTimePicker1.value}
              onChange={handleChangeTime1}
              bordered={false}
              format="HH:mm:ss"
              allowClear
              disabled={disabledTimePicker.value}
              showNow={false}
              v-slots={{
                suffixIcon: () => null
              }}
            />
            <SwapRightOutlined class={style['icon-arrow']} />
            <TimePicker
              class="flex-1"
              popupClassName="date-time-range-picker-time-picker"
              placeholder="结束时间"
              getPopupContainer={getPopupContainer}
              v-model:value={vlaueTimePicker2.value}
              v-model:open={openTimePicker2.value}
              onChange={handleChangeTime2}
              bordered={false}
              format="HH:mm:ss"
              allowClear
              disabled={disabledTimePicker.value}
              showNow={false}
              v-slots={{
                suffixIcon: () => null
              }}
            />
          </div>
          {!attrs.ranges ? (
            <div class="text-right">
              <Button type="primary" size="small" onClick={handleConfirm}>
                确定
              </Button>
            </div>
          ) : null}
        </>
      )
    };

    return () => (
      <RangePicker
        class={clsDateTimeRangePicker}
        format={props.format}
        dropdownClassName={`${clsDateTimeRangePickerDropdown} date-time-range-picker-dropdown`}
        open={openRangePicker.value}
        value={value.value}
        // @ts-ignore
        onCalendarChange={handleCalendarChange}
        onOpenChange={handleOpenChange}
        v-slots={slots}
      />
    );
  }
});

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