/** @jsxImportSource hono/jsx */
import { define, stylesToCSS } from '@because/forge'

// ---------------------------------------------------------------------------
// Forge components
// ---------------------------------------------------------------------------

const Page = define('Page', {
  fontFamily: '-apple-system, system-ui, sans-serif',
  background: '#111',
  color: '#fff',
  height: '100dvh',
  width: '100vw',
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'center',
  justifyContent: 'center',
  overflow: 'hidden',
  userSelect: 'none',
  WebkitUserSelect: 'none',
  position: 'relative',
  touchAction: 'manipulation',
})

const Screen = define('Screen', {
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'center',
  justifyContent: 'center',
  flex: 1,
  width: '100%',
  padding: 20,
})

const Title = define('Title', {
  fontSize: 18,
  color: '#888',
  textAlign: 'center',
  lineHeight: 1.4,
})

const GuessDisplay = define('GuessDisplay', {
  fontSize: 96,
  fontWeight: 'bold',
  fontFamily: 'monospace',
  lineHeight: 1,
  margin: '24px 0',
})

const Controls = define('Controls', {
  display: 'flex',
  gap: 12,
  marginBottom: 24,
})

const StepBtn = define('StepBtn', {
  base: 'button',
  fontSize: 24,
  fontWeight: 'bold',
  width: 64,
  height: 64,
  border: 'none',
  borderRadius: 12,
  background: '#333',
  color: '#fff',
  cursor: 'pointer',
  WebkitTapHighlightColor: 'transparent',
  touchAction: 'manipulation',
  states: { ':active': { background: '#555' } },
})

const GuessBtn = define('GuessBtn', {
  base: 'button',
  fontSize: 24,
  fontWeight: 'bold',
  padding: '16px 48px',
  border: 'none',
  borderRadius: 16,
  background: '#22c55e',
  color: '#fff',
  cursor: 'pointer',
  WebkitTapHighlightColor: 'transparent',
  touchAction: 'manipulation',
  states: { ':active': { background: '#16a34a' } },
})

const HintText = define('HintText', {
  fontSize: 36,
  fontWeight: 'bold',
  marginTop: 32,
  minHeight: 50,
  display: 'flex',
  alignItems: 'center',
  transition: 'opacity 0.15s ease',
})

const VictoryEmoji = define('VictoryEmoji', {
  fontSize: 64,
  marginBottom: 16,
})

const VictoryText = define('VictoryText', {
  fontSize: 22,
  lineHeight: 1.5,
  textAlign: 'center',
  padding: '0 20px',
  maxWidth: 380,
})

const StatusBar = define('StatusBar', {
  position: 'fixed',
  bottom: 0,
  left: 0,
  right: 0,
  padding: 8,
  fontSize: 12,
  color: '#555',
  textAlign: 'center',
})

const VolumeWarning = define('VolumeWarning', {
  fontSize: 14,
  color: '#f59e0b',
  textAlign: 'center',
  marginTop: 16,
  padding: '8px 16px',
  background: 'rgba(245, 158, 11, 0.1)',
  borderRadius: 8,
  border: '1px solid rgba(245, 158, 11, 0.3)',
})

const ConnectBtn = define('ConnectBtn', {
  base: 'button',
  fontSize: 28,
  fontWeight: 'bold',
  padding: '20px 48px',
  border: 'none',
  borderRadius: 16,
  background: '#2563eb',
  color: '#fff',
  cursor: 'pointer',
  WebkitTapHighlightColor: 'transparent',
  touchAction: 'manipulation',
  states: { ':active': { background: '#1d4ed8' } },
})

const ConnectStatus = define('ConnectStatus', {
  fontSize: 18,
  color: '#888',
  marginTop: 24,
  textAlign: 'center',
})

// ---------------------------------------------------------------------------
// Client-side code — real functions, serialized into <script>
// ---------------------------------------------------------------------------

/* eslint-disable */
// @ts-nocheck — this function is serialized and runs in the browser, not on the server
function clientMain() {
  var currentGuess = 50
  var playing = false
  var busy = false
  var gg, ggInstance, audioContext
  var pendingResolve

  var connectScreen = document.getElementById('connect-screen')
  var connectStatus = document.getElementById('connect-status')
  var guessDisplay = document.getElementById('guess-display')
  var hintArea = document.getElementById('hint-area')
  var statusEl = document.getElementById('status')
  var guessBtn = document.getElementById('guess-btn')
  var gameScreen = document.getElementById('game-screen')
  var victoryScreen = document.getElementById('victory-screen')
  var victoryText = document.getElementById('victory-text')

  function bypassSilentMode() {
    if ('audioSession' in navigator) navigator.audioSession.type = 'play-and-record'
  }

  function sleep(ms) {
    return new Promise(function (resolve) { setTimeout(resolve, ms) })
  }

  function playChirp(text) {
    var rawBytes = gg.encode(
      ggInstance, text,
      gg.ProtocolId.GGWAVE_PROTOCOL_AUDIBLE_FAST, 50
    )
    var bytesCopy = new Uint8Array(rawBytes.length)
    bytesCopy.set(new Uint8Array(rawBytes.buffer, rawBytes.byteOffset, rawBytes.length))
    var floats = new Float32Array(bytesCopy.buffer)

    var buf = audioContext.createBuffer(1, floats.length, audioContext.sampleRate)
    buf.getChannelData(0).set(floats)
    var source = audioContext.createBufferSource()
    source.buffer = buf
    source.connect(audioContext.destination)

    return new Promise(function (resolve) { source.onended = resolve; source.start() })
  }

  async function sendAndWait(text, timeoutMs) {
    playing = true
    if (audioContext.state === 'suspended') await audioContext.resume()
    await playChirp(text)
    await sleep(500)
    playing = false

    return new Promise(function (resolve) {
      var timer
      if (timeoutMs) {
        timer = setTimeout(function () {
          pendingResolve = undefined
          resolve(undefined)
        }, timeoutMs)
      }
      pendingResolve = function (msg) {
        if (timer) clearTimeout(timer)
        pendingResolve = undefined
        resolve(msg)
      }
    })
  }

  function startListening(stream) {
    var source = audioContext.createMediaStreamSource(stream)
    var processor = audioContext.createScriptProcessor(4096, 1, 1)
    var samplesPerFrame = gg.getDefaultParameters().samplesPerFrame
    var buffer = new Float32Array(0)

    processor.onaudioprocess = function (e) {
      if (playing) return

      var input = e.inputBuffer.getChannelData(0)
      var newBuf = new Float32Array(buffer.length + input.length)
      newBuf.set(buffer)
      newBuf.set(input, buffer.length)
      buffer = newBuf

      while (buffer.length >= samplesPerFrame) {
        var frame = buffer.slice(0, samplesPerFrame)
        buffer = buffer.slice(samplesPerFrame)

        var bytes = new Uint8Array(frame.buffer, frame.byteOffset, frame.byteLength)
        var result = gg.decode(ggInstance, bytes)
        if (result && result.length > 0) {
          var text = Array.from(result)
            .map(function (b) { return String.fromCharCode(b & 0xff) })
            .join('')
          if (pendingResolve) pendingResolve(text)
        }
      }
    }

    source.connect(processor)
    processor.connect(audioContext.destination)
  }

  async function connect() {
    try {
      // AudioContext MUST be created synchronously in gesture handler (iOS)
      audioContext = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: 48000 })
      var src = audioContext.createBufferSource()
      src.buffer = audioContext.createBuffer(1, 1, 48000)
      src.connect(audioContext.destination)
      src.start()
      bypassSilentMode()

      connectStatus.textContent = 'Initializing audio (' + audioContext.sampleRate + ' Hz)...'
      gg = await ggwave_factory()
      gg.disableLog()
      var ggParams = gg.getDefaultParameters()
      ggParams.sampleRateInp = audioContext.sampleRate
      ggParams.sampleRateOut = audioContext.sampleRate
      ggParams.sampleRate = audioContext.sampleRate
      ggInstance = gg.init(ggParams)

      connectStatus.textContent = 'Requesting microphone...'
      var stream = await navigator.mediaDevices.getUserMedia({
        audio: { echoCancellation: false, noiseSuppression: false, autoGainControl: false },
      })
      startListening(stream)

      connectStatus.textContent = 'Connecting via audio...'
      var response = await sendAndWait('HELLO', 8000)

      if (response === 'HEY BUDDY') {
        connectScreen.style.display = 'none'
        gameScreen.style.display = 'flex'
        statusEl.textContent = 'Ready'
      } else {
        connectStatus.textContent = 'No response heard. Make sure the server is running and volume is up!'
        connectStatus.style.color = '#ef4444'
      }
    } catch (err) {
      connectStatus.textContent = err.message || 'Failed to connect'
      connectStatus.style.color = '#ef4444'
    }
  }

  async function sendGuess() {
    if (busy || !gg) return
    busy = true
    guessBtn.textContent = 'Sending...'
    guessBtn.style.background = '#666'
    hintArea.textContent = ''
    hintArea.style.opacity = '0'

    statusEl.textContent = 'Listening for response...'
    guessBtn.textContent = 'Listening...'
    var response = await sendAndWait(String(currentGuess), 8000)

    if (response === 'Higher!' || response === 'Lower!') {
      hintArea.style.opacity = '0'
      hintArea.textContent = response
      hintArea.style.color = response === 'Higher!' ? '#f59e0b' : '#3b82f6'
      void hintArea.offsetWidth
      hintArea.style.opacity = '1'
    } else if (response === 'Correct!') {
      gameScreen.style.display = 'none'
      victoryScreen.style.display = 'flex'
      victoryText.textContent = 'You found ' + currentGuess + '!'
      statusEl.textContent = ''
    } else {
      hintArea.textContent = 'No response heard. Volume up on both devices!'
      hintArea.style.color = '#ef4444'
      hintArea.style.opacity = '1'
    }

    busy = false
    guessBtn.textContent = 'Guess!'
    guessBtn.style.background = '#22c55e'
    statusEl.textContent = 'Ready'
  }

  function adjust(delta) {
    if (busy) return
    currentGuess = Math.max(1, Math.min(100, currentGuess + delta))
    guessDisplay.textContent = currentGuess
    hintArea.textContent = ''
    hintArea.style.opacity = '0'
  }

  window.connect = connect
  window.adjust = function (delta) { adjust(delta) }
  window.sendGuess = function () { sendGuess() }
}

// ---------------------------------------------------------------------------
// Phone page component
// ---------------------------------------------------------------------------

export function PhonePage() {
  return (
    <html>
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
        <title>Corey's Screechy Audio Demo</title>
        <link rel="stylesheet" href="/styles.css" />
        <script src="/ggwave.js">{""}</script>
      </head>
      <body style="margin:0;overflow:hidden;touch-action:manipulation">
        <Page>
          <Screen id="connect-screen">
            <Title>Corey's Screechy Audio Demo</Title>
            <VolumeWarning>🔊 Turn your volume up on both devices!</VolumeWarning>
            <div style="margin-top:32px">
              <ConnectBtn onclick="connect()">Connect</ConnectBtn>
            </div>
            <ConnectStatus id="connect-status" />
          </Screen>

          <Screen id="game-screen" style="display:none">
            <Title>Guess the Number<br />1 – 100</Title>
            <GuessDisplay id="guess-display">50</GuessDisplay>
            <Controls>
              <StepBtn onclick="adjust(-10)">-10</StepBtn>
              <StepBtn onclick="adjust(-1)">-1</StepBtn>
              <StepBtn onclick="adjust(1)">+1</StepBtn>
              <StepBtn onclick="adjust(10)">+10</StepBtn>
            </Controls>
            <GuessBtn id="guess-btn" onclick="sendGuess()">Guess!</GuessBtn>
            <HintText id="hint-area" />
          </Screen>

          <Screen id="victory-screen" style="display:none">
            <VictoryEmoji>🎉</VictoryEmoji>
            <VictoryText id="victory-text" />
          </Screen>

          <StatusBar id="status" />
        </Page>
        <script dangerouslySetInnerHTML={{ __html: `(${clientMain})()` }} />
      </body>
    </html>
  )
}

export { stylesToCSS }
