"use client"
import React, { useMemo } from "react"
import { createSeededRNG } from "../../utils/random"
import { useCountWithInterval, useTick } from "./hooks"
import { tween } from "./utils"

type Layer = {
  text: string
  type: "base" | "random"
  pattern: boolean[]
}

export type CipherTextProps = {
  children: string

  active?: boolean
  paused?: boolean
  interval?: number

  charSet?: string
  infinite?: boolean

  progress?: number
  initialProgress?: number
}

export function CipherText({
  children: text,
  active = true,
  paused = false,
  charSet = "-_~`!@#$%^&*()+=[]{}|;:,.<>?",
  interval: intervalMs = 50,
  infinite = false,
  progress: forcedProgress,
  initialProgress = 0,
}: CipherTextProps) {
  const size = text.length

  /**
   * Progress for the text layer
   */
  const autoProgress = useCountWithInterval(initialProgress, {
    max: size,
    paused,
    increase: active,
    intervalMs,
  })

  const progress = forcedProgress ?? autoProgress

  /**
   * Progress for the infinite layer
   */
  const infiniteProgress = useCountWithInterval(infinite ? size : 0, {
    max: size,
    increase: infinite,
    intervalMs,
  })

  useTick(infiniteProgress > 0, intervalMs)

  /**
   * Determining layers
   */
  const random = useMemo(() => createSeededRNG(text), [text])
  const percentComplete = progress / size
  const baseRandomLayer = new Array(size).fill("")

  const layers = [
    // Base text layer
    {
      text,
      pattern: new Array(size).fill(false).map((_, i) => progress > i),
    },
    // Cipher layer
    {
      text: baseRandomLayer.map(
        () => charSet[Math.floor(random() * charSet.length)],
      ),
      pattern: baseRandomLayer.map((_, i) =>
        progress > i ? random() > tween(0.25, 1, percentComplete) : false,
      ),
    },
    // Infinite layer (just random cipher if enabled)
    infiniteProgress > 0
      ? {
          text: baseRandomLayer.map(
            () => charSet[Math.floor(random() * charSet.length)],
          ),
          pattern: new Array(size)
            .fill(false)
            .map(() => infiniteProgress / size > random()),
        }
      : undefined,
  ].filter(Boolean) as Layer[]

  /**
   * Merging layers into a single string
   */
  const mergedLayers = useMemo(() => {
    const text = new Array(size).fill("")

    for (const layer of layers) {
      for (let i = 0; i < layer.text.length; i++) {
        if (layer.pattern?.[i] === false) {
          continue
        }

        text[i] = layer.text[i]
      }
    }

    return text.join("").trim()
  }, [layers, size])

  return <>{mergedLayers}</>
}
