<template>
  <view 
    class="nana-bubble-box"
    :style="outerStyle"
    @mouseenter="handleHover(true)"
    @mouseleave="handleHover(false)"
  >
    <view v-if="trigger === 'click'" @click.native.capture="handleClick">
      <slot />
    </view>
    <slot v-else />
    <SimpleTransition name="bubble-box" :show="showState" :duration="200">
      <template #show="{ classNames }">
        <view class="nana-bubble-box-popup-mask" @click="hide" />
        <FlexView
          position="absolute"
          :direction="direction"
          :backgroundColor="backgroundColor"
          :radius="radius"
          :gap="10"
          :padding="10"
          :zIndex="1001"
          :margin="selectObjectByType(position, 'left', {
            top: [arrowWidth,0],
            bottom: [arrowWidth,0],
            left: [0,arrowWidth],
            right: [0,arrowWidth],
          })"
          :innerClass="['nana-bubble-box-popup',position,classNames]"
          shadow="light"
          v-bind="innerProps"
          :innerStyle="innerStyle"
        >
          <view 
            class="nana-bubble-box-arrow" 
            :style="{ 
              borderWidth: theme.resolveThemeSize(arrowWidth),
              borderColor: backgroundColor,
              borderRightColor: 'transparent',
              borderBottomColor: 'transparent',
              borderLeftColor: 'transparent',
            }"
          />
          <slot name="content">
            <Touchable
              v-for="item in items"
              :key="item.text"
              direction="row"
              align-items="center"
              :gap="10"
              :padding="[5, 20]"
              v-bind="itemProps"
              @click="handleItemClick(item)"
            >
              <Icon
                :name="item.icon"
                :size="44"
                :color="item.textColor || itemTextColor"
                v-bind="{ ...itemIconProps, ...item.iconProps }"
              />
              <Text 
                :wrap="false" 
                v-bind="itemTextProps" 
                :color="item.textColor || itemTextColor"
                :text="item.text" 
              />
            </Touchable>
          </slot>
        </FlexView>
      </template>
    </SimpleTransition>
  </view>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';
import { propGetThemeVar, useTheme } from '../theme/ThemeDefine';
import { selectObjectByType, selectStyleType } from '../theme/ThemeTools';
import type { FlexProps } from '../layout/FlexView.vue';
import type { TextProps } from '../basic/Text.vue';
import Icon, { type IconProps } from '../basic/Icon.vue';
import FlexView from '../layout/FlexView.vue';
import Text from '../basic/Text.vue';
import Touchable from './Touchable.vue';
import SimpleTransition from '../anim/SimpleTransition.vue';

export interface BubbleBoxItem {
  text: string,
  textColor?: string,
  icon?: string,
  iconProps?: IconProps,
  onClick: () => void,
}
export interface BubbleBoxProps {
  /**
   * 气泡框位置
   * @default top
   */
  position?: 'left' | 'right' | 'top' | 'bottom',
  /**
   * 气泡在横轴上的对齐位置，默认居中
   * @default center
   */
  crossPosition?: 'left' | 'center' | 'right',
  /**
   * 触发点击事件模式
   * @default click
   */
  trigger?: 'click'|'hover'|'none',
  /**
   * 气泡框按钮排列方向
   * @default column
   */
  direction?: 'column' | 'row',
  /**
   * 是否禁用
   * @default false
   */
  disabled?: boolean,
  /**
   * 气泡框按钮数组
   */
  items?: BubbleBoxItem[],
  /**
   * 气泡框按钮文本颜色
   * @default 'text.content'
   */
  itemTextColor?: string,
  /**
   * 气泡框按钮文本样式
   */
  itemTextProps?: TextProps,
  /**
   * 气泡框按钮图标样式
   */
  itemIconProps?: IconProps,
  /**
   * 气泡框按钮样式
   */
  itemProps?: FlexProps,
  /**
   * 气泡框外层容器样式
   */
  outerStyle?: Record<string, string>,
  /**
   * 气泡框背景颜色
   * @default white
   */
  backgroundColor?: string,
  /**
   * 气泡框箭头宽度
   * @default 12
   */
  arrowWidth?: number,
  /**
   * 气泡框圆角半径
   * @default 12
   */
  radius?: number,
  /**
   * 气泡框内层容器样式
   */
  innerProps?: FlexProps,
  /**
   * 气泡框内层容器样式
   */
  innerStyle?: Record<string, string>,
}
export interface BubbleBoxExpose {
  show: () => void,
  hide: () => void,
}
const theme = useTheme();
const props = withDefaults(defineProps<BubbleBoxProps>(), {
  position: 'top',
  trigger: 'click',
  direction: 'column',
  arrowWidth: () => propGetThemeVar('BubbleBoxArrowWidth', 12),
  items: () => [],
  itemTextColor: () => propGetThemeVar('BubbleBoxItemTextColor', 'text.content'),
  backgroundColor: () => propGetThemeVar('BubbleBoxBackgroundColor', 'white'),
  radius: () => propGetThemeVar('BubbleBoxRadius', 12),
});

const backgroundColor = computed(() => theme.resolveThemeColor(props.backgroundColor));
const showState = ref(false);
const lock = ref(false);

function handleItemClick(item: BubbleBoxItem) {
  hide();
  item.onClick();
}
function handleClick() {
  if (lock.value) return;
  if (props.trigger === 'click' && !props.disabled)  {
     enterLock();
    showState.value = !showState.value;
  }
}
function handleHover(show: boolean) {
  if (lock.value) return;
  if (props.trigger === 'hover' && !props.disabled)
    showState.value = show;
}

function enterLock() {
  lock.value = true;
  setTimeout(() => {
    lock.value = false;
  }, 300);
}
function show() { 
  showState.value = true;
  enterLock();
}
function hide() { 
  showState.value = false; 
  enterLock();
}

const innerStyle = computed(() => {
  const horzLayout = selectStyleType(props.crossPosition, 'left', {
    left: {
      k: 'top',
      y: '0%',
      t: 'translateY(0%)',
    },
    center: {
      k: 'top',
      y: '50%',
      t: 'translateY(-50%)',
    },
    right: {
      k: 'bottom',
      y: '0',
      t: 'translateY(0%)',
    }
  });
  const vertLayout = selectStyleType(props.crossPosition, 'left', {
    left: {
      k: 'left',
      x: '0%',
      t: 'translateX(0%)',
    },
    center: {
      k: 'left',
      x: '50%',
      t: 'translateX(-50%)',
    },
    right: {
      k: 'right',
      x: '0%',
      t: 'translateX(0%)',
    }
  });
  return {
    ...props.innerStyle,
    ...selectStyleType(props.position, 'top', {
      left: {
        [horzLayout.k]: horzLayout.y,
        right: '100%',
        transform: horzLayout.t + ' translateX(0)',
      },
      right: {
        [horzLayout.k]: horzLayout.y,
        left: '100%',
        transform: horzLayout.t + '',
      },
      top: {
        top: '0%',
        [vertLayout.k]: vertLayout.x,
        transform: vertLayout.t + ' translateY(-100%)',
      },
      bottom: {
        bottom: '0%',
        [vertLayout.k]: vertLayout.x,
        transform: vertLayout.t + ' translateY(100%)',
      }
    })
  }
})

defineExpose<BubbleBoxExpose>({
  show,
  hide,
})

defineOptions({
  options: {
    virtualHost: true,
    styleIsolation: "shared",
  }
})
</script>

<style lang="scss">
.nana-bubble-box {
  position: relative;
  overflow: visible;

  .nana-bubble-box-popup {
    position: absolute;
    transition: opacity ease-in-out 0.2s;

    &.left {
      .nana-bubble-box-arrow {
        top: 50%;
        left: 100%;
        transform: translateY(-50%) rotate(-90deg);
      }
    }
    &.right {
      .nana-bubble-box-arrow {
        top: 50%;
        left: 0;
        transform: translateX(-100%) translateY(-50%) rotate(90deg);
      }
    }
    &.top {
      .nana-bubble-box-arrow {
        top: 100%;
        left: 50%;
        transform: translateX(-50%) rotate(0);
      }
    }
    &.bottom {
      .nana-bubble-box-arrow {
        top: 0;
        left: 50%;
        transform: translateX(-50%) translateY(-100%) rotate(180deg);
      }
    }
 
    &.bubble-box-enter-active,
    &.bubble-box-leave-active {
      opacity: 1;
    }
    &.bubble-box-enter-from,
    &.bubble-box-leave-to {
      opacity: 0;
    }
  }
  .nana-bubble-box-popup-mask {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1000;
  }
  .nana-bubble-box-arrow {
    position: absolute;
    width: 0;
    height: 0;
    border-style: solid;
  }
}
</style>
