<script>
import theme from "#build/b24ui/countdown";
</script>

<script setup>
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";
import { Primitive } from "reka-ui";
import { useAppConfig } from "#imports";
import { useComponentIcons } from "../composables/useComponentIcons";
import { tv } from "../utils/tv";
import B24Avatar from "./Avatar.vue";
const MILLISECONDS_SECOND = 1e3;
const MILLISECONDS_MINUTE = 60 * MILLISECONDS_SECOND;
const MILLISECONDS_HOUR = 60 * MILLISECONDS_MINUTE;
const MILLISECONDS_DAY = 24 * MILLISECONDS_HOUR;
defineOptions({ inheritAttrs: false });
const props = defineProps({
  as: { type: null, required: false, default: "span" },
  size: { type: null, required: false },
  emitEvents: { type: Boolean, required: false, default: true },
  seconds: { type: [Number, String], required: false, default: 0 },
  showMinutes: { type: Boolean, required: false, default: true },
  useCircle: { type: Boolean, required: false, default: false },
  interval: { type: Number, required: false, default: 1e3 },
  needStartImmediately: { type: Boolean, required: false, default: true },
  now: { type: Function, required: false, default: () => Date.now() },
  class: { type: null, required: false },
  b24ui: { type: null, required: false },
  icon: { type: [Function, Object], required: false },
  avatar: { type: Object, required: false }
});
const emits = defineEmits(["start", "end", "abort", "progress"]);
defineSlots();
const { isLeading, leadingIconName } = useComponentIcons(
  computed(() => ({ ...props, loading: false }))
);
const appConfig = useAppConfig();
const b24ui = computed(() => tv({ extend: tv(theme), ...appConfig.b24ui?.countdown || {} })({
  size: props.size,
  leading: Boolean(isLeading.value),
  useCircle: Boolean(props.useCircle)
}));
const counting = ref(false);
const endTime = ref(0);
const totalMilliseconds = ref(0);
const requestId = ref(0);
onMounted(() => {
  document.addEventListener("visibilitychange", handleVisibilityChange.bind(this));
});
onBeforeUnmount(() => {
  document.removeEventListener("visibilitychange", handleVisibilityChange.bind(this));
  pause();
});
watch(
  () => props,
  () => {
    totalMilliseconds.value = Number(props.seconds) * 1e3;
    endTime.value = props.now() + Number(props.seconds) * 1e3;
    if (props.needStartImmediately) {
      start();
    }
  },
  {
    deep: true,
    immediate: true
  }
);
const days = computed(() => {
  return Math.floor(totalMilliseconds.value / MILLISECONDS_DAY);
});
const hours = computed(() => {
  return Math.floor(totalMilliseconds.value % MILLISECONDS_DAY / MILLISECONDS_HOUR);
});
const minutes = computed(() => {
  return Math.floor(totalMilliseconds.value % MILLISECONDS_HOUR / MILLISECONDS_MINUTE);
});
const secondsValue = computed(() => {
  return Math.floor(totalMilliseconds.value % MILLISECONDS_MINUTE / MILLISECONDS_SECOND);
});
const milliseconds = computed(() => {
  return Math.floor(totalMilliseconds.value % MILLISECONDS_SECOND);
});
const totalDays = computed(() => {
  return days.value;
});
const totalHours = computed(() => {
  return Math.floor(totalMilliseconds.value / MILLISECONDS_HOUR);
});
const totalMinutes = computed(() => {
  return Math.floor(totalMilliseconds.value / MILLISECONDS_MINUTE);
});
const totalSeconds = computed(() => {
  return Math.floor(totalMilliseconds.value / MILLISECONDS_SECOND);
});
const formatTime = computed(() => {
  if (props.showMinutes && !props.useCircle) {
    return `${totalMinutes.value < 10 ? "0" : ""}${totalMinutes.value}:${secondsValue.value < 10 ? "0" : ""}${secondsValue.value}`;
  } else if (props.useCircle) {
    return `:${totalSeconds.value}`;
  }
  return String(totalSeconds.value);
});
function start() {
  if (counting.value) {
    return;
  }
  counting.value = true;
  if (!props.needStartImmediately) {
    totalMilliseconds.value = Number(props.seconds) * 1e3;
    endTime.value = props.now() + Number(props.seconds) * 1e3;
  }
  if (props.emitEvents) {
    emits("start");
  }
  if (document?.visibilityState === "visible") {
    continueProcess();
  }
}
function continueProcess() {
  if (!counting.value) {
    return;
  }
  const delay = Math.min(totalMilliseconds.value, props.interval);
  if (delay > 0) {
    let init;
    let prev;
    const step = (now) => {
      if (!init) {
        init = now;
      }
      if (!prev) {
        prev = now;
      }
      const range = now - init;
      if (range >= delay || range + (now - prev) / 2 >= delay) {
        progress();
      } else {
        requestId.value = requestAnimationFrame(step);
      }
      prev = now;
    };
    requestId.value = requestAnimationFrame(step);
  } else {
    stop();
  }
}
function pause() {
  cancelAnimationFrame(requestId.value);
}
function progress() {
  if (!counting.value) {
    return;
  }
  update();
  if (props.emitEvents && totalMilliseconds.value > 0) {
    emits("progress", {
      days: days.value,
      hours: hours.value,
      minutes: minutes.value,
      seconds: secondsValue.value,
      milliseconds: milliseconds.value,
      totalDays: totalDays.value,
      totalHours: totalHours.value,
      totalMinutes: totalMinutes.value,
      totalSeconds: totalSeconds.value,
      totalMilliseconds: totalMilliseconds.value
    });
  }
  continueProcess();
}
function abort() {
  if (!counting.value) {
    return;
  }
  pause();
  counting.value = false;
  if (props.emitEvents) {
    emits("abort");
  }
}
function stop() {
  if (!counting.value) {
    return;
  }
  pause();
  totalMilliseconds.value = 0;
  counting.value = false;
  if (props.emitEvents) {
    emits("end");
  }
}
function update() {
  if (counting.value) {
    totalMilliseconds.value = Math.max(0, endTime.value - props.now());
  }
}
function restart() {
  pause();
  totalMilliseconds.value = Number(props.seconds) * 1e3;
  endTime.value = props.now() + Number(props.seconds) * 1e3;
  counting.value = false;
  start();
}
function handleVisibilityChange() {
  switch (document?.visibilityState) {
    case "visible":
      update();
      continueProcess();
      break;
    case "hidden":
      pause();
      break;
  }
}
const fullDashArray = computed(() => {
  const fullDashArray2 = 283;
  const calculateTimeFraction = () => {
    if (Number(props.seconds) < 0) {
      return 1;
    }
    const rawTimeFraction = totalSeconds.value / Number(props.seconds);
    return rawTimeFraction - 1 / Number(props.seconds) * (1 - rawTimeFraction);
  };
  return [
    (calculateTimeFraction() * fullDashArray2).toFixed(0),
    fullDashArray2
  ].join(" ");
});
defineExpose({
  start,
  abort,
  stop,
  restart
});
</script>

<template>
  <Primitive
    :as="as"
    v-bind="$attrs"
    data-slot="base"
    :class="b24ui.base({ class: [props.b24ui?.base, props.class] })"
  >
    <svg
      v-if="props.useCircle"
      data-slot="circleBase"
      :class="b24ui.circleBase({ class: [props.b24ui?.circleBase] })"
      viewBox="0 0 100 100"
      xmlns="http://www.w3.org/2000/svg"
    >
      <g
        data-slot="circleGroup"
        :class="b24ui.circleGroup({ class: [props.b24ui?.circleGroup] })"
      >
        <circle
          data-slot="circleElement"
          :class="b24ui.circleElement({ class: [props.b24ui?.circleElement] })"
          cx="50"
          cy="50"
          r="45"
        />
        <path
          data-slot="circlePath"
          :class="b24ui.circlePath({ class: [props.b24ui?.circlePath] })"
          :stroke-dasharray="fullDashArray"
          d="M 50, 50 m -45, 0 a 45,45 0 1,0 90,0 a 45,45 0 1,0 -90,0"
        />
      </g>
    </svg>
    <slot name="leading" :b24ui="b24ui">
      <Component
        :is="leadingIconName"
        v-if="isLeading && typeof leadingIconName !== 'undefined'"
        data-slot="leadingIcon"
        :class="b24ui.leadingIcon({ class: props.b24ui?.leadingIcon })"
      />
      <B24Avatar
        v-else-if="!!avatar"
        :size="props.b24ui?.leadingAvatarSize || b24ui.leadingAvatarSize()"
        v-bind="avatar"
        data-slot="leadingAvatar"
        :class="b24ui.leadingAvatar({ class: props.b24ui?.leadingAvatar })"
      />
    </slot>
    <slot
      :days="days"
      :hours="hours"
      :minutes="minutes"
      :seconds="secondsValue"
      :milliseconds="milliseconds"
      :total-days="totalDays"
      :total-hours="totalHours"
      :total-minutes="totalMinutes"
      :total-seconds="totalSeconds"
      :total-milliseconds="totalMilliseconds"
      :format-time="formatTime"
      :b24ui="b24ui"
    >
      <span data-slot="label" :class="b24ui.label({ class: props.b24ui?.label })">
        {{ formatTime }}
      </span>
    </slot>
  </Primitive>
</template>
