import * as Blockly from 'blockly';
import './blocks/esp32_blocks.js';
import './blocks/esp32_generators.js';
import './blocks/arduino_generators.js';
import { buildToolbox, getDeviceId } from './devices/registry.js';
import { loadAllSavedAddons } from './addons/loader.js';
import { initLoginDialog } from './ui/loginDialog.js';
import {
  getUser,
  getToken,
  refreshMe,
  onAuthChange,
  logout,
} from './auth/client.js';
import { connectTeacher, disconnectTeacher, sendTeacher } from './ws/teacherClient.js';
import './style.css';
import './teacher.css';

loadAllSavedAddons();

const MAIN_WORKSPACE_KEY = 'esp32block_workspace';

const statusEl = document.getElementById('teacher-status');
const countEl = document.getElementById('teacher-count');
const gridEl = document.getElementById('teacher-grid');
const emptyEl = document.getElementById('teacher-empty');
const badgeEl = document.getElementById('user-badge');
const logoutBtn = document.getElementById('teacher-logout');
const overlayEl = document.getElementById('student-overlay');
const modalTitle = document.getElementById('student-modal-title');
const modalPreview = document.getElementById('student-modal-preview');
const modalClose = document.getElementById('student-modal-close');
const modalPushBtn = document.getElementById('student-push-btn');
const modalStatus = document.getElementById('student-modal-status');
const hiddenWsDiv = document.getElementById('teacher-hidden-ws');
const createUsernameInput = document.getElementById('teacher-new-student-username');
const createStudentBtn = document.getElementById('teacher-create-student');
const generateUsernameBtn = document.getElementById('teacher-generate-username');
const createStudentStatus = document.getElementById('teacher-create-student-status');

// Map userId -> { username, state, online, tileEl, blockly, emptyLabel }
const students = new Map();
let modalBlockly = null;
let focusedUserId = null;

// Hidden workspace that mirrors the teacher's own IDE state from localStorage.
// This is the "my current blocks" that gets pushed to students.
let hiddenWorkspace = null;
let modalResizeHandler = null;

function ensureHiddenWorkspace() {
  if (hiddenWorkspace) return hiddenWorkspace;
  hiddenWsDiv.style.width = '800px';
  hiddenWsDiv.style.height = '600px';
  hiddenWorkspace = Blockly.inject(hiddenWsDiv, {
    toolbox: buildToolbox(getDeviceId()),
    readOnly: true,
    theme: Blockly.Themes.Dark,
    renderer: 'zelos',
  });
  loadFromStorageIntoHidden();
  window.addEventListener('storage', (e) => {
    if (e.key === MAIN_WORKSPACE_KEY) loadFromStorageIntoHidden();
  });
  return hiddenWorkspace;
}

function loadFromStorageIntoHidden() {
  if (!hiddenWorkspace) return;
  const raw = localStorage.getItem(MAIN_WORKSPACE_KEY);
  if (!raw) return;
  try {
    const state = JSON.parse(raw);
    Blockly.serialization.workspaces.load(state, hiddenWorkspace);
  } catch { /* ignore */ }
}

function getTeacherCurrentState() {
  ensureHiddenWorkspace();
  loadFromStorageIntoHidden();
  try {
    return Blockly.serialization.workspaces.save(hiddenWorkspace);
  } catch {
    return null;
  }
}

function setStatus(text, cls) {
  if (!statusEl) return;
  statusEl.textContent = text;
  statusEl.className = cls || '';
}

function updateCount() {
  const total = students.size;
  const online = Array.from(students.values()).filter((s) => s.online).length;
  countEl.textContent = `${online} online / ${total} total`;
  emptyEl.classList.toggle('hidden', total > 0);
}

function createTile(userId, username) {
  const tile = document.createElement('div');
  tile.className = 'student-tile';
  tile.dataset.userId = String(userId);

  const header = document.createElement('div');
  header.className = 'student-tile-header';

  const nameSpan = document.createElement('span');
  nameSpan.className = 'student-tile-name';
  nameSpan.textContent = username;
  const statusSpan = document.createElement('span');
  statusSpan.className = 'student-tile-status';
  statusSpan.textContent = '● live';

  header.appendChild(nameSpan);
  header.appendChild(statusSpan);

  const blocklyHost = document.createElement('div');
  blocklyHost.className = 'student-tile-blockly';

  const emptyLabel = document.createElement('div');
  emptyLabel.className = 'student-tile-empty';
  emptyLabel.textContent = 'No blocks yet';
  blocklyHost.appendChild(emptyLabel);

  tile.appendChild(header);
  tile.appendChild(blocklyHost);

  tile.addEventListener('click', () => openStudentModal(userId));
  gridEl.appendChild(tile);

  const blockly = Blockly.inject(blocklyHost, {
    readOnly: true,
    theme: Blockly.Themes.Dark,
    renderer: 'zelos',
    zoom: { controls: false, wheel: false, startScale: 0.45 },
    trashcan: false,
    scrollbars: false,
    move: { scrollbars: false, drag: false, wheel: false },
  });

  return { tile, blockly, blocklyHost, emptyLabel, nameSpan, statusSpan };
}

function updateTileOnlineState(entry) {
  entry.statusSpan.textContent = entry.online ? '● live' : '● offline';
  entry.statusSpan.classList.toggle('offline', !entry.online);
}

function applyStateToWorkspace(state, blocklyWs) {
  try {
    if (state && state.blocks && Array.isArray(state.blocks.blocks) && state.blocks.blocks.length > 0) {
      Blockly.serialization.workspaces.load(state, blocklyWs);
      return true;
    }
    blocklyWs.clear();
    return false;
  } catch {
    blocklyWs.clear();
    return false;
  }
}

function upsertStudent({ userId, username, state, online = false }) {
  let entry = students.get(userId);
  if (!entry) {
    const parts = createTile(userId, username);
    entry = {
      userId,
      username,
      state: null,
      online,
      ...parts,
    };
    students.set(userId, entry);
    updateCount();
  } else if (entry.username !== username) {
    entry.username = username;
    entry.nameSpan.textContent = username;
  }
  entry.online = !!online;
  updateTileOnlineState(entry);
  entry.state = state || null;
  const hasBlocks = applyStateToWorkspace(entry.state, entry.blockly);
  entry.emptyLabel.style.display = hasBlocks ? 'none' : '';
  Blockly.svgResize(entry.blockly);

  if (focusedUserId === userId) {
    refreshModalFromEntry(entry);
  }
}

function removeStudent(userId) {
  const entry = students.get(userId);
  if (!entry) return;
  try { entry.blockly.dispose(); } catch { /* ignore */ }
  entry.tile.remove();
  students.delete(userId);
  updateCount();
  if (focusedUserId === userId) closeStudentModal();
}

function clearAllStudents() {
  for (const id of Array.from(students.keys())) removeStudent(id);
}

function setCreateStatus(text, isError = false) {
  if (!createStudentStatus) return;
  createStudentStatus.textContent = text || '';
  createStudentStatus.className = `login-status${isError ? ' status-err' : ' status-ok'}`;
}

function apiUrl(path) {
  const base = import.meta.env.BASE_URL || '/';
  const p = path.startsWith('/') ? path.slice(1) : path;
  return (base.endsWith('/') ? base : base + '/') + p;
}

async function teacherRequest(path, { method = 'GET', body } = {}) {
  const token = getToken();
  const headers = { 'Content-Type': 'application/json' };
  if (token) headers.Authorization = `Bearer ${token}`;
  const res = await fetch(apiUrl(path), {
    method,
    headers,
    body: body ? JSON.stringify(body) : undefined,
  });
  let data = null;
  try { data = await res.json(); } catch { /* ignore */ }
  if (!res.ok) throw new Error(data?.error || `HTTP ${res.status}`);
  return data;
}

async function loadAllStudentsFromServer() {
  try {
    const data = await teacherRequest('/api/teacher/students');
    for (const s of data.students || []) {
      upsertStudent({
        userId: s.id,
        username: s.username,
        state: null,
        online: false,
      });
    }
    updateCount();
  } catch {
    // ignore; websocket still provides live students
  }
}

function refreshModalFromEntry(entry) {
  if (!modalBlockly) return;
  modalTitle.textContent = entry.username;
  applyStateToWorkspace(entry.state, modalBlockly);
  Blockly.svgResize(modalBlockly);
}

function openStudentModal(userId) {
  const entry = students.get(userId);
  if (!entry) return;
  focusedUserId = userId;
  overlayEl.classList.remove('hidden');
  modalStatus.textContent = '';
  modalStatus.className = 'login-status';

  if (!modalBlockly) {
    modalBlockly = Blockly.inject(modalPreview, {
      readOnly: true,
      theme: Blockly.Themes.Dark,
      renderer: 'zelos',
      zoom: { controls: true, wheel: true, startScale: 0.85 },
      trashcan: false,
    });
    modalResizeHandler = () => {
      if (modalBlockly) Blockly.svgResize(modalBlockly);
    };
    window.addEventListener('resize', modalResizeHandler);
  }

  requestAnimationFrame(() => {
    refreshModalFromEntry(entry);
  });
}

function closeStudentModal() {
  focusedUserId = null;
  overlayEl.classList.add('hidden');
}

modalClose.addEventListener('click', closeStudentModal);
overlayEl.addEventListener('click', (e) => {
  if (e.target === overlayEl) closeStudentModal();
});

modalPushBtn.addEventListener('click', () => {
  if (focusedUserId == null) return;
  const state = getTeacherCurrentState();
  if (!state) {
    modalStatus.textContent = 'No local blocks found to push. Open the main IDE first.';
    modalStatus.className = 'login-status status-err';
    return;
  }
  modalPushBtn.disabled = true;
  modalStatus.textContent = 'Pushing…';
  modalStatus.className = 'login-status';
  const ok = sendTeacher({ type: 'push-code', targetUserId: focusedUserId, state });
  if (!ok) {
    modalStatus.textContent = 'Not connected.';
    modalStatus.className = 'login-status status-err';
    modalPushBtn.disabled = false;
  }
});

function handleMessage(msg) {
  if (msg.type === 'roster') {
    for (const entry of students.values()) {
      entry.online = false;
      updateTileOnlineState(entry);
    }
    for (const s of msg.students || []) {
      upsertStudent({ ...s, online: true });
    }
    updateCount();
    return;
  }
  if (msg.type === 'student-update') {
    upsertStudent({ userId: msg.userId, username: msg.username, state: msg.state, online: true });
    updateCount();
    return;
  }
  if (msg.type === 'student-leave') {
    const entry = students.get(msg.userId);
    if (entry) {
      entry.online = false;
      updateTileOnlineState(entry);
      updateCount();
      if (focusedUserId === msg.userId) refreshModalFromEntry(entry);
    }
    return;
  }
  if (msg.type === 'push-code-result') {
    modalPushBtn.disabled = false;
    if (msg.ok) {
      modalStatus.textContent = 'Pushed!';
      modalStatus.className = 'login-status status-ok';
    } else {
      modalStatus.textContent = msg.error || 'Push failed';
      modalStatus.className = 'login-status status-err';
    }
    return;
  }
}

function renderBadge(user) {
  if (!badgeEl) return;
  if (user) {
    badgeEl.textContent = `${user.username}${user.role === 'teacher' ? ' (teacher)' : ''}`;
    badgeEl.classList.remove('hidden');
  } else {
    badgeEl.textContent = '';
    badgeEl.classList.add('hidden');
  }
}

function connectIfTeacher() {
  const user = getUser();
  const token = getToken();
  renderBadge(user);
  clearAllStudents();

  if (!user || !token) {
    setStatus('Not signed in', 'status-disconnected');
    disconnectTeacher();
    openLoginModal();
    return;
  }
  if (user.role !== 'teacher') {
    setStatus('Teacher account required', 'status-disconnected');
    disconnectTeacher();
    return;
  }

  setStatus('Connecting…', 'status-disconnected');
  loadAllStudentsFromServer().catch(() => { /* ignore */ });
  connectTeacher({
    token,
    onOpen: () => {
      setStatus('Connected', 'status-connected');
      sendTeacher({ type: 'list' });
    },
    onClose: () => setStatus('Disconnected', 'status-disconnected'),
    onMessage: handleMessage,
  });
}

async function suggestUsername() {
  try {
    const data = await teacherRequest('/api/teacher/students/suggest-username');
    if (createUsernameInput) createUsernameInput.value = data.username || '';
    setCreateStatus('');
  } catch (err) {
    setCreateStatus(err.message || 'Could not generate username', true);
  }
}

async function createStudentQuick() {
  const username = (createUsernameInput?.value || '').trim();
  createStudentBtn.disabled = true;
  try {
    const data = await teacherRequest('/api/teacher/students', {
      method: 'POST',
      body: username ? { username } : {},
    });
    if (createUsernameInput) createUsernameInput.value = data.student?.username || '';
    upsertStudent({
      userId: data.student.id,
      username: data.student.username,
      state: null,
      online: false,
    });
    updateCount();
    setCreateStatus(`Created ${data.student.username} (password: ${data.defaultPassword})`);
  } catch (err) {
    setCreateStatus(err.message || 'Could not create student', true);
  } finally {
    createStudentBtn.disabled = false;
  }
}

function openLoginModal() {
  const overlay = document.getElementById('login-overlay');
  if (overlay) overlay.classList.remove('hidden');
}

logoutBtn?.addEventListener('click', async () => {
  await logout();
});

initLoginDialog();
onAuthChange(() => connectIfTeacher());
ensureHiddenWorkspace();
updateCount();

generateUsernameBtn?.addEventListener('click', () => {
  suggestUsername().catch(() => { /* ignore */ });
});

createStudentBtn?.addEventListener('click', () => {
  createStudentQuick().catch(() => { /* ignore */ });
});

refreshMe()
  .then(() => connectIfTeacher())
  .catch(() => connectIfTeacher());
