<template>
  <div class="characters-stage" style="width:550px;height:400px">
    <!-- Red tall character - back layer -->
    <div ref="redRef" class="ch" :style="redStyle">
      <div class="eyes-wrap" :style="redEyesStyle">
        <div class="eye" :style="{ width: '18px', height: isRedBlink ? '2px' : '18px' }">
          <div class="pupil-dot" style="width:7px;height:7px" />
        </div>
        <div class="eye" :style="{ width: '18px', height: isRedBlink ? '2px' : '18px' }">
          <div class="pupil-dot" style="width:7px;height:7px" />
        </div>
      </div>
    </div>

    <!-- Dark character - middle layer -->
    <div ref="darkRef" class="ch" :style="darkStyle">
      <div class="eyes-wrap" :style="darkEyesStyle">
        <div class="eye" :style="{ width: '16px', height: isDarkBlink ? '2px' : '16px' }">
          <div class="pupil-dot" style="width:6px;height:6px" />
        </div>
        <div class="eye" :style="{ width: '16px', height: isDarkBlink ? '2px' : '16px' }">
          <div class="pupil-dot" style="width:6px;height:6px" />
        </div>
      </div>
    </div>

    <!-- Terracotta semi-circle - front left -->
    <div ref="terraRef" class="ch" :style="terraStyle">
      <div class="eyes-wrap" style="gap:32px" :style="terraEyesStyle">
        <div class="pup-solo" style="width:12px;height:12px" />
        <div class="pup-solo" style="width:12px;height:12px" />
      </div>
    </div>

    <!-- Gold semi-circle - front right -->
    <div ref="goldRef" class="ch" :style="goldStyle">
      <div class="eyes-wrap" style="gap:32px" :style="goldEyesStyle">
        <div class="pup-solo" style="width:12px;height:12px" />
        <div class="pup-solo" style="width:12px;height:12px" />
      </div>
      <div class="mouth" :style="mouthStyle" />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, computed, onMounted, onUnmounted, watch } from "vue";

const props = defineProps<{
  isTyping?: boolean;
  showPassword?: boolean;
  passwordLength?: number;
}>();

// Mouse position
const mx = ref(0);
const my = ref(0);
function onMouseMove(e: MouseEvent) { mx.value = e.clientX; my.value = e.clientY; }

// Blink state
const isRedBlink = ref(false);
const isDarkBlink = ref(false);

// Behavior state
const isLook = ref(false);

// Refs
const redRef = ref<HTMLElement>();
const darkRef = ref<HTMLElement>();
const terraRef = ref<HTMLElement>();
const goldRef = ref<HTMLElement>();

// Computed flags
const hidePw = computed(() => (props.passwordLength ?? 0) > 0 && !props.showPassword);
const showPw = computed(() => (props.passwordLength ?? 0) > 0 && props.showPassword);

// Face tracking data
const red = reactive({ fx: 0, fy: 0, sk: 0 });
const dark = reactive({ fx: 0, fy: 0, sk: 0 });
const terra = reactive({ fx: 0, fy: 0, sk: 0 });
const gold = reactive({ fx: 0, fy: 0, sk: 0 });

function calcFace(el: HTMLElement | undefined) {
  if (!el) return { fx: 0, fy: 0, sk: 0 };
  const r = el.getBoundingClientRect();
  return {
    fx: Math.max(-15, Math.min(15, (mx.value - (r.left + r.width / 2)) / 20)),
    fy: Math.max(-10, Math.min(10, (my.value - (r.top + r.height / 3)) / 30)),
    sk: Math.max(-6, Math.min(6, -(mx.value - (r.left + r.width / 2)) / 120)),
  };
}

// Vue 3.4+ 推荐使用 getter 函数数组
watch([() => mx.value, () => my.value], () => {
  Object.assign(red, calcFace(redRef.value));
  Object.assign(dark, calcFace(darkRef.value));
  Object.assign(terra, calcFace(terraRef.value));
  Object.assign(gold, calcFace(goldRef.value));
});

// --- Character styles ---

const redStyle = computed(() => ({
  left: '70px',
  width: '180px',
  height: props.isTyping || hidePw.value ? '440px' : '400px',
  backgroundColor: '#C83A35',
  borderRadius: '10px 10px 0 0',
  zIndex: 1,
  transform: showPw.value
    ? 'skewX(0deg)'
    : props.isTyping || hidePw.value
      ? `skewX(${red.sk - 12}deg) translateX(40px)`
      : `skewX(${red.sk}deg)`,
  transformOrigin: 'bottom center',
}));

const redEyesStyle = computed(() => ({
  left: `${showPw.value ? 20 : isLook.value ? 55 : 45 + red.fx}px`,
  top: `${showPw.value ? 35 : isLook.value ? 65 : 40 + red.fy}px`,
}));

const darkStyle = computed(() => ({
  left: '240px',
  width: '120px',
  height: '310px',
  backgroundColor: '#2D2D2D',
  borderRadius: '8px 8px 0 0',
  zIndex: 2,
  transform: showPw.value
    ? 'skewX(0deg)'
    : isLook.value
      ? `skewX(${dark.sk * 1.5 + 10}deg) translateX(20px)`
      : `skewX(${dark.sk}deg)`,
  transformOrigin: 'bottom center',
}));

const darkEyesStyle = computed(() => ({
  left: `${showPw.value ? 10 : isLook.value ? 32 : 26 + dark.fx}px`,
  top: `${showPw.value ? 28 : isLook.value ? 12 : 32 + dark.fy}px`,
}));

const terraStyle = computed(() => ({
  left: '0px',
  width: '240px',
  height: '200px',
  zIndex: 3,
  backgroundColor: '#c4958a',
  borderRadius: '120px 120px 0 0',
  transform: showPw.value ? 'skewX(0deg)' : `skewX(${terra.sk}deg)`,
  transformOrigin: 'bottom center',
}));

const terraEyesStyle = computed(() => ({
  left: `${showPw.value ? 50 : 82 + terra.fx}px`,
  top: `${showPw.value ? 85 : 90 + terra.fy}px`,
}));

const goldStyle = computed(() => ({
  left: '310px',
  width: '140px',
  height: '230px',
  backgroundColor: '#c4a86a',
  borderRadius: '70px 70px 0 0',
  zIndex: 4,
  transform: showPw.value ? 'skewX(0deg)' : `skewX(${gold.sk}deg)`,
  transformOrigin: 'bottom center',
}));

const goldEyesStyle = computed(() => ({
  left: `${showPw.value ? 20 : 52 + gold.fx}px`,
  top: `${showPw.value ? 35 : 40 + gold.fy}px`,
}));

const mouthStyle = computed(() => ({
  left: `${showPw.value ? 10 : 40 + gold.fx}px`,
  top: `${showPw.value ? 88 : 88 + gold.fy}px`,
}));

// --- Typing reaction ---
watch(
  () => props.isTyping,
  (v) => {
    if (v) {
      isLook.value = true;
      window.setTimeout(() => { isLook.value = false; }, 800);
    }
  }
);

// --- Password peek reaction ---
let peekT = 0;
watch(
  [() => props.passwordLength, () => props.showPassword],
  ([len, show]) => {
    clearTimeout(peekT);
    if ((len ?? 0) > 0 && show) {
      const sched = () => {
        peekT = window.setTimeout(() => {
          peekT = window.setTimeout(() => { sched(); }, 800);
        }, Math.random() * 3000 + 2000);
      };
      sched();
    }
  }
);

// --- Blink timers ---
const timers: number[] = [];
function scheduleBlink(setter: (v: boolean) => void) {
  const tick = () => {
    timers.push(window.setTimeout(() => {
      setter(true);
      timers.push(window.setTimeout(() => { setter(false); tick(); }, 150));
    }, Math.random() * 4000 + 3000));
  };
  tick();
}

onMounted(() => {
  window.addEventListener("mousemove", onMouseMove);
  scheduleBlink((v) => (isRedBlink.value = v));
  scheduleBlink((v) => (isDarkBlink.value = v));
});

onUnmounted(() => {
  window.removeEventListener("mousemove", onMouseMove);
  timers.forEach(clearTimeout);
  clearTimeout(peekT);
});
</script>

<style scoped>
.characters-stage { position: relative; }
.ch {
  position: absolute;
  bottom: 0;
  transition: all 0.7s ease-in-out;
}
.eyes-wrap {
  position: absolute;
  display: flex;
  transition: all 0.2s ease-out;
}
.eye {
  background: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  transition: height 0.15s ease;
}
.pupil-dot {
  background: #2D2D2D;
  border-radius: 50%;
}
.pup-solo {
  background: #2D2D2D;
  border-radius: 50%;
}
.mouth {
  position: absolute;
  width: 80px;
  height: 4px;
  background: #2D2D2D;
  border-radius: 999px;
  transition: all 0.2s ease-out;
}
</style>
