<template>
  <div class="level-page">
    <header class="level-header">
      <router-link to="/levels" class="back-link">&larr; Back</router-link>
      <div class="hdr-center">
        <span class="level-badge">Level {{ level.id }}</span>
        <h1>{{ level.title }}</h1>
      </div>
      <div class="hdr-stars">
        <span v-for="s in 3" :key="s" :class="{ earned: s <= bestStars }">&#9733;</span>
      </div>
    </header>

    <div class="level-layout">
      <TutorialPanel :level="level" :visible="showTut" @toggle="showTut = !showTut" class="panel-tut" />

      <div class="panel-center">
        <CodeEditor v-model="code" :currentLine="curLine" :errorLine="errLine" :readOnly="mode !== 'edit'" />
        <div class="controls">
          <div class="ctrls-left">
            <button class="cb run" @click="runCode" :disabled="mode === 'auto'">&#9654; Run</button>
            <button class="cb step" @click="stepCode" :disabled="mode === 'auto'">
              &rarr;{{ mode === 'step' ? ' Next' : ' Step' }}
            </button>
            <button v-if="mode === 'edit'" class="cb auto" @click="autoStep">&#9193; Animate</button>
            <button v-if="mode !== 'edit'" class="cb reset" @click="resetVM">&#8634; Reset</button>
          </div>
          <div v-if="mode === 'auto'" class="speed">
            <label>Speed</label>
            <input type="range" min="50" max="800" :value="800-delay" @input="delay=800-+$event.target.value">
          </div>
        </div>
        <OutputConsole :output="out" :error="vmErr" :status="statusMsg" @clear="clearOut" />
      </div>

      <MachineState :registers="regs" :flags="fl" :memory="mem" :stack="stk"
                     :showMemory="level.showMemory" :memoryRange="level.memoryRange"
                     :changes="changes" class="panel-state" />
    </div>

    <div class="hints-bar">
      <button class="hint-btn" @click="nextHint" :disabled="hintIdx >= level.hints.length - 1">
        &#128161; Hint ({{ Math.min(hintIdx+2, level.hints.length) }}/{{ level.hints.length }})
      </button>
      <div v-if="hintIdx >= 0" class="hint-text">{{ level.hints[hintIdx] }}</div>
    </div>

    <LevelComplete v-if="showComp" :level="level" :stars="earnedStars" :instructionCount="iCount"
                   @next="goNext" @retry="doRetry" @close="showComp = false" />
  </div>
</template>

<script setup>
import { ref, reactive, computed, onUnmounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useGameStore } from '../stores/game.js'
import { levels } from '../lib/levels.js'
import { createVM, countInstructions, validateLevel, parse } from '../lib/vm.js'
import TutorialPanel from '../components/TutorialPanel.vue'
import CodeEditor from '../components/CodeEditor.vue'
import OutputConsole from '../components/OutputConsole.vue'
import MachineState from '../components/MachineState.vue'
import LevelComplete from '../components/LevelComplete.vue'

const props = defineProps({ id: [String, Number] })
const router = useRouter()
const store = useGameStore()
if (!store.isLoggedIn) router.replace('/')

const level = computed(() => levels.find(l => l.id === +props.id) || levels[0])
const bestStars = computed(() => store.progress[level.value.id]?.stars || 0)

const code = ref(store.progress[level.value.id]?.code || level.value.starterCode)
const mode = ref('edit')
const showTut = ref(true)
const showComp = ref(false)
const earnedStars = ref(0)
const hintIdx = ref(-1)
const delay = ref(200)

const EMPTY_REGS = { R0:0,R1:0,R2:0,R3:0,R4:0,R5:0,R6:0,R7:0 }
const regs = reactive({ ...EMPTY_REGS })
const fl = reactive({ zero:false, carry:false, negative:false, overflow:false })
const mem = ref(new Array(256).fill(0))
const stk = ref([])
const out = ref([])
const vmErr = ref('')
const statusMsg = ref('')
const curLine = ref(-1)
const errLine = ref(-1)
const changes = ref([])
const iCount = ref(0)

let vm = null
let autoTimer = null

function applyInitState() {
  Object.assign(regs, EMPTY_REGS)
  const m = new Array(256).fill(0)
  const is = level.value.initialState
  if (is) {
    if (is.registers) Object.assign(regs, is.registers)
    if (is.memory) for (const [a,v] of Object.entries(is.memory)) m[+a] = v
  }
  mem.value = m
  fl.zero = false; fl.carry = false; fl.negative = false; fl.overflow = false
  stk.value = []; out.value = []
}

function initVM() {
  vm = createVM()
  if (level.value.blockedOps) {
    const p = parse(code.value)
    for (const i of p.instructions) {
      if (level.value.blockedOps.includes(i.opcode)) {
        vmErr.value = `This level doesn't allow ${i.opcode}.`; return false
      }
    }
  }
  const r = vm.loadProgram(code.value)
  if (vm.state.error) { vmErr.value = vm.state.error; if (r.errors?.length) errLine.value = r.errors[0].line; return false }
  const is = level.value.initialState
  if (is) {
    if (is.registers) Object.assign(vm.state.registers, is.registers)
    if (is.memory) for (const [a,v] of Object.entries(is.memory)) vm.state.memory[+a] = v
    if (is.input) vm.state.input = [...is.input]
  }
  syncState(); return true
}

function syncState() {
  if (!vm) return
  Object.assign(regs, vm.state.registers)
  Object.assign(fl, vm.state.flags)
  mem.value = [...vm.state.memory]
  stk.value = [...vm.state.stack]
  out.value = [...vm.state.output]
  if (vm.state.error) vmErr.value = vm.state.error
}

function runCode() {
  stopAuto(); vmErr.value = ''; errLine.value = -1; mode.value = 'run'
  if (!initVM()) { mode.value = 'edit'; return }
  vm.run(); syncState(); curLine.value = -1
  iCount.value = countInstructions(code.value)
  if (vm.state.error) {
    statusMsg.value = 'Runtime error'
    if (vm.state.pc < vm.state.instructions.length) errLine.value = vm.state.instructions[vm.state.pc]?.srcLine ?? -1
    return
  }
  checkResult()
}

function stepCode() {
  if (mode.value === 'edit' || mode.value === 'run') {
    vmErr.value = ''; errLine.value = -1; mode.value = 'step'
    if (!initVM()) { mode.value = 'edit'; return }
    statusMsg.value = 'Stepping...'
  }
  if (vm.state.halted || vm.state.error) { iCount.value = countInstructions(code.value); checkResult(); return }
  const r = vm.step()
  if (r) { changes.value = r.changes || []; curLine.value = r.srcLine ?? -1 }
  syncState()
  if (vm.state.halted || vm.state.error) { iCount.value = countInstructions(code.value); if (vm.state.halted) checkResult() }
}

function autoStep() {
  vmErr.value = ''; errLine.value = -1; mode.value = 'auto'
  if (!initVM()) { mode.value = 'edit'; return }
  statusMsg.value = 'Animating...'; doAuto()
}

function doAuto() {
  if (!vm || vm.state.halted || vm.state.error || mode.value !== 'auto') {
    if (vm?.state.halted) { iCount.value = countInstructions(code.value); checkResult() }
    syncState(); return
  }
  const r = vm.step()
  if (r) { changes.value = r.changes || []; curLine.value = r.srcLine ?? -1 }
  syncState()
  autoTimer = setTimeout(doAuto, delay.value)
}

function stopAuto() { if (autoTimer) { clearTimeout(autoTimer); autoTimer = null } }

function resetVM() {
  stopAuto(); vm = null; mode.value = 'edit'
  curLine.value = -1; errLine.value = -1; vmErr.value = ''; statusMsg.value = ''
  changes.value = []; applyInitState()
}

function clearOut() { out.value = []; vmErr.value = ''; statusMsg.value = '' }

function checkResult() {
  const tv = createVM(); const lp = tv.loadProgram(code.value)
  if (lp.errors && lp.errors.length > 0) {
    vmErr.value = lp.errors[0].msg; statusMsg.value = 'Not passing yet — try again!'
    return
  }
  const r = validateLevel(level.value, tv)
  if (r.passed) {
    iCount.value = countInstructions(code.value)
    const [s3, s2] = level.value.starThresholds
    earnedStars.value = iCount.value <= s3 ? 3 : iCount.value <= s2 ? 2 : 1
    store.saveProgress(level.value.id, earnedStars.value, code.value)
    showComp.value = true; statusMsg.value = 'Level complete!'
  } else {
    vmErr.value = r.msg; statusMsg.value = 'Not passing yet — try again!'
  }
}

function nextHint() { if (hintIdx.value < level.value.hints.length - 1) hintIdx.value++ }
function goNext() { showComp.value = false; const n = level.value.id + 1; router.push(n <= 10 ? `/level/${n}` : '/levels') }
function doRetry() { showComp.value = false; resetVM() }

watch(() => props.id, () => {
  stopAuto(); vm = null; mode.value = 'edit'
  code.value = store.progress[level.value.id]?.code || level.value.starterCode
  hintIdx.value = -1; showComp.value = false; curLine.value = -1; errLine.value = -1
  vmErr.value = ''; statusMsg.value = ''; changes.value = []; applyInitState()
})

applyInitState()
onUnmounted(() => stopAuto())
</script>

<style scoped>
.level-page { height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
.level-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 20px; background: var(--bg-card); border-bottom: 1px solid var(--border); flex-shrink: 0; }
.back-link { color: var(--text-secondary); font-size: 14px; min-width: 60px; }
.hdr-center { text-align: center; }
.level-badge { font-size: 12px; color: var(--text-muted); }
.level-header h1 { font-size: 18px; font-weight: 600; }
.hdr-stars { display: flex; gap: 4px; font-size: 20px; min-width: 60px; justify-content: flex-end; }
.hdr-stars span { color: var(--border); }
.hdr-stars span.earned { color: var(--accent-yellow); }

.level-layout { flex: 1; display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; padding: 10px; overflow: hidden; min-height: 0; }
.panel-tut { overflow-y: auto; min-height: 0; }
.panel-center { display: flex; flex-direction: column; gap: 6px; min-height: 0; overflow: hidden; }
.panel-center .code-editor { flex: 1; min-height: 0; overflow: hidden; }
.panel-state { overflow-y: auto; min-height: 0; }

.controls { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-shrink: 0; }
.ctrls-left { display: flex; gap: 5px; }
.cb { padding: 6px 12px; font-size: 13px; font-weight: 500; }
.cb.run { background: var(--accent-green); color: #fff; }
.cb.run:hover { background: #0d9668; }
.cb.step { background: var(--accent-blue); color: #fff; }
.cb.step:hover { background: #2563eb; }
.cb.auto { background: var(--accent-purple); color: #fff; }
.cb.auto:hover { background: #7c3aed; }
.cb.reset { background: var(--bg-surface); color: var(--text-primary); border: 1px solid var(--border); }
.cb.reset:hover { background: var(--bg-hover); }
.cb:disabled { opacity: 0.5; cursor: not-allowed; }
.speed { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-muted); }
.speed input[type="range"] { width: 100px; accent-color: var(--accent-purple); }

.hints-bar { display: flex; align-items: center; gap: 12px; padding: 7px 20px; background: var(--bg-card); border-top: 1px solid var(--border); flex-shrink: 0; }
.hint-btn { padding: 5px 14px; background: rgba(245,158,11,0.1); color: var(--accent-yellow); border: 1px solid rgba(245,158,11,0.3); font-size: 13px; white-space: nowrap; flex-shrink: 0; }
.hint-btn:hover:not(:disabled) { background: rgba(245,158,11,0.2); }
.hint-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.hint-text { font-size: 13px; color: var(--accent-yellow); font-family: var(--font-mono); }

@media (max-width: 1024px) {
  .level-layout { grid-template-columns: 1fr; }
  .panel-tut { max-height: 200px; }
  .panel-state { max-height: 250px; }
}
</style>
