<script setup lang="ts">
import { ref, computed, watch, defineEmits, defineProps, onMounted, nextTick, defineExpose } from 'vue'
import Input from '~/components/pub/ui/input/Input.vue';
import Button from '~/components/pub/ui/button/Button.vue';
import waveyFingerprint from '~/assets/svg/wavey-fingerprint.svg'

/**
 * TextCaptcha props:
 * - length: number of characters in the core captcha
 * - caseSensitive: whether validation is case sensitive
 * - useSpacing: show spaced-out characters (visual obfuscation only)
 * - noiseChars: include random noise characters visually (not required to type)
 */
const props = defineProps({
  length: { type: Number, default: 6 },
  caseSensitive: { type: Boolean, default: false },
  useSpacing: { type: Boolean, default: true },
  noiseChars: { type: Boolean, default: false }, // adds random noise characters to display
  refreshCooldownMs: { type: Number, default: 500 }, // guard repeated refresh
})

const emit = defineEmits<{
  (e: 'update:valid', valid: boolean): void
  (e: 'validated', valid: boolean): void
  (e: 'change', value: string): void
}>()

// Internal state
const raw = ref('')            // the canonical captcha value (what user must match, ignoring visual noise)
const display = ref('')        // randomized visual representation (may include spacing/noise)
const input = ref('')          // user typed value
const lastRefresh = ref(0)
const valid = inject('isCaptchaValid') as Ref<boolean>
const errorMessage = ref('')

/** Characters excluding ambiguous ones: 0/O, 1/l/I etc. */
const CHARS = '23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'

function randomChar() {
  return CHARS.charAt(Math.floor(Math.random() * CHARS.length))
}

/** Generate the canonical captcha string */
function genRaw(len = props.length) {
  let s = ''
  for (let i = 0; i < len; i++) s += randomChar()
  return s
}

/** Create a visually obfuscated display string (spacing, noise, random case) */
function genDisplay(base: string) {
  const arr: string[] = []
  for (const ch of base) {
    // toggle case randomly (only for letters)
    const c = /[A-Za-z]/.test(ch) && Math.random() > 0.5 ? (Math.random() > 0.5 ? ch.toLowerCase() : ch.toUpperCase()) : ch
    arr.push(c)
    if (props.useSpacing && Math.random() > 0.3) arr.push(' ') // random space
  }
  return arr.join('')
}

/** Refresh captcha */
function refresh() {
  const now = Date.now()
  if (now - lastRefresh.value < props.refreshCooldownMs) return
  lastRefresh.value = now

  raw.value = genRaw(props.length)
  display.value = genDisplay(raw.value)
  input.value = ''
  valid.value = false
  errorMessage.value = ''
  // emit change so parent knows new value (but we don't send the raw canonical in production)
  emit('change', display.value)
}

/** Normalize input and canonical for comparison */
function normalizeForCompare(s: string) {
  const normalized = s.replace(/\s+/g, '') // strip spaces
  return props.caseSensitive ? normalized : normalized.toLowerCase()
}

/** Validate the current input */
function validate() {
  const left = normalizeForCompare(input.value)
  const right = normalizeForCompare(raw.value)
  if (!input.value) {
    valid.value = false
    errorMessage.value = 'Please enter the captcha text.'
  } else if (left === right) {
    valid.value = true
    errorMessage.value = ''
  } else {
    valid.value = false
    errorMessage.value = 'Captcha does not match.'
  }
  emit('update:valid', valid.value)
  emit('validated', valid.value)
  return valid.value
}

// expose a refresh method to parent via ref
defineExpose({ refresh, validate, isValid: computed(() => valid.value) })

// generate on mount
onMounted(() => refresh())

// // re-validate whenever input changes (lightweight)
// watch(input, () => {
//   // we don't auto-pass until the user explicitly validate (but we can optionally live-validate)
//   // Here we perform live feedback but still emit validated only when called
//   const left = normalizeForCompare(input.value)
//   const right = normalizeForCompare(raw.value)
//   valid.value = !!input.value && left === right
//   // emit a live update so the parent can disable submit accordingly
//   emit('update:valid', valid.value)
// })
</script>

<template>
  <div class="space-y-2 w-full max-w-sm">
    <div class="flex items-center justify-between gap-3">
      <!-- Captcha visual box -->
      <div
        role="img"
        aria-label="Text captcha, type the characters shown"
        tabindex="0"
        class="select-none p-3 rounded-md border border-gray-200 text-white text-xl font-mono tracking-wider text-center w-full"
      >
        <span class="inline-block" v-html="display"></span>
      </div>

      <!-- Refresh -->
      <div class="flex-shrink-0">
        <Button variant="ghost" type="button" @click="refresh" title="Refresh captcha">
          <Icon name="i-lucide-refresh-cw" />
        </Button>
      </div>
    </div>

    <!-- Input -->
    <div class="flex gap-3 items-start">
        <div class="flex-grow">
            <Input
                v-model="input"
                :aria-invalid="valid ? 'false' : 'true'"
                inputmode="text"
                placeholder="Type the captcha text"
                @keyup.enter="validate"
            />
            <p v-if="errorMessage" class="text-xs text-red-500 mt-1">{{ errorMessage }}</p>
            <p v-else-if="valid" class="text-xs text-green-500 mt-1">Correct</p>
            <p v-else class="text-xs text-gray-500 mt-1">Not case-sensitive</p>
        </div>
        <Button variant="outline" type="button" @click="validate" title="Validate"
            class="border-orange-400">
            <Icon name="i-lucide-check" class="text-orange-400" />
        </Button>
    </div>

  </div>
</template>

<style scoped>
/* small nicety: make noise/spaced display look irregular */
div[role="img"] {
  background: url('~/assets/svg/wavey-fingerprint.svg') repeat center;
}

div[role="img"] span {
  letter-spacing: 0.12em;
  font-weight: 600;
  user-select: none;
}
</style>