import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
import * as http from 'http';
import * as os from 'os';
import * as crypto from 'crypto';
import { getToolWebviewStyles, handleClipboardAndOpen } from './shared';

const store: { panel: vscode.WebviewPanel | null } = { panel: null };

interface ActiveShare {
  server: http.Server;
  port: number;
  token: string;
  files: SharedFile[];
}

interface SharedFile {
  buf: Buffer;
  filename: string;
}

export function openSendToMobile(context: vscode.ExtensionContext) {
  if (store.panel) {
    store.panel.reveal();
    return;
  }

  const panel = vscode.window.createWebviewPanel(
    'hplToolbox.sendToMobile',
    'Send To Mobile',
    vscode.ViewColumn.Active,
    {
      enableScripts: true,
      retainContextWhenHidden: true,
      localResourceRoots: [vscode.Uri.joinPath(context.extensionUri, 'media')],
    }
  );
  store.panel = panel;

  let active: ActiveShare | null = null;

  const stopActive = () => {
    if (active) {
      try { active.server.close(); } catch { /* noop */ }
      active = null;
    }
  };

  panel.onDidDispose(() => {
    stopActive();
    store.panel = null;
  });

  const qrUri = panel.webview.asWebviewUri(
    vscode.Uri.joinPath(context.extensionUri, 'media', 'qrcode.min.js')
  );
  panel.webview.html = getHtml(qrUri.toString());

  panel.webview.onDidReceiveMessage(async (msg) => {
    try {
      if (handleClipboardAndOpen(msg)) return;

      if (msg.type === 'pickFolder') {
        const picked = await vscode.window.showOpenDialog({
          canSelectFolders: true,
          canSelectFiles: false,
          canSelectMany: false,
          openLabel: 'Select Folder',
          defaultUri: getPickerDefaultUri(),
        });
        if (picked && picked[0]) {
          const files = await collectHtmlFiles(picked[0].fsPath);
          panel.webview.postMessage({
            type: 'fileSelected',
            files: files.map(filePath => ({ path: filePath, name: path.basename(filePath) })),
          });
        }
        return;
      }

      if (msg.type === 'pickFile') {
        const picked = await vscode.window.showOpenDialog({
          canSelectMany: true,
          filters: { HTML: ['html', 'htm'] },
          openLabel: 'Select',
          defaultUri: getPickerDefaultUri(),
        });
        if (picked && picked.length) {
          panel.webview.postMessage({
            type: 'fileSelected',
            files: picked.map(u => ({ path: u.fsPath, name: path.basename(u.fsPath) })),
          });
        }
        return;
      }

      if (msg.type === 'startShare') {
        stopActive();
        const filePaths: string[] = Array.isArray(msg.paths) ? msg.paths : (msg.path ? [msg.path] : []);
        if (!filePaths.length) {
          panel.webview.postMessage({ type: 'error', message: 'File missing.' });
          return;
        }
        const files: SharedFile[] = [];
        for (let i = 0; i < filePaths.length; i++) {
          const filePath = filePaths[i];
          if (!filePath || !fs.existsSync(filePath)) {
            panel.webview.postMessage({ type: 'error', message: 'File missing.' });
            return;
          }
          panel.webview.postMessage({ type: 'status', message: `Loading ${i + 1}/${filePaths.length} ${path.basename(filePath)}` });
          files.push({ buf: fs.readFileSync(filePath), filename: path.basename(filePath) });
        }
        panel.webview.postMessage({ type: 'status', message: 'Starting server...' });
        const token = crypto.randomBytes(6).toString('base64url');

        const cfg = vscode.workspace.getConfiguration('hplToolbox.sendToMobile');
        const configuredPort = cfg.get<number>('port') ?? 0;

        const share: ActiveShare = {
          server: http.createServer(),
          port: 0,
          token,
          files,
        };
        share.server.on('request', (req, res) => handleRequest(req, res, share));
        share.server.on('error', (err) => {
          panel.webview.postMessage({ type: 'error', message: 'Server error: ' + err.message });
          stopActive();
        });

        share.server.listen(configuredPort, '0.0.0.0', () => {
          const addr = share.server.address();
          if (!addr || typeof addr === 'string') {
            panel.webview.postMessage({ type: 'error', message: 'Failed to bind server.' });
            return;
          }
          share.port = addr.port;
          active = share;

          const ips = getLanIps();
          if (ips.length === 0) {
            panel.webview.postMessage({
              type: 'error',
              message: 'No non-loopback IPv4 interface found. Are you on a network?',
            });
            stopActive();
            return;
          }
          const urls = ips.map((ip) => ({
            ip: ip.address,
            iface: ip.iface,
            url: `http://${ip.address}:${share.port}/s/${token}/`,
          }));
          panel.webview.postMessage({
            type: 'sharing',
            urls,
            filename: files.length === 1 ? files[0].filename : `${files.length} files`,
          });
        });
        return;
      }

      if (msg.type === 'stopShare') {
        stopActive();
        panel.webview.postMessage({ type: 'stopped' });
        return;
      }
    } catch (err: any) {
      panel.webview.postMessage({ type: 'error', message: err?.message || String(err) });
    }
  });
}

async function collectHtmlFiles(root: string): Promise<string[]> {
  const out: string[] = [];
  const stack: string[] = [root];
  while (stack.length) {
    const dir = stack.pop()!;
    let entries: fs.Dirent[];
    try {
      entries = fs.readdirSync(dir, { withFileTypes: true });
    } catch {
      continue;
    }
    for (const e of entries) {
      const full = path.join(dir, e.name);
      if (e.isDirectory()) {
        if (e.name === 'node_modules' || e.name === '.git') continue;
        stack.push(full);
      } else if (e.isFile() && /\.html?$/i.test(e.name)) {
        out.push(full);
      }
    }
  }
  return out;
}

function handleRequest(req: http.IncomingMessage, res: http.ServerResponse, share: ActiveShare) {
  if (req.method !== 'GET' || !req.url) {
    res.statusCode = 404;
    res.end();
    return;
  }
  const prefix = `/s/${share.token}`;
  if (!req.url.startsWith(prefix)) {
    res.statusCode = 404;
    res.end();
    return;
  }
  const rest = req.url.slice(prefix.length).replace(/\?.*$/, '');

  if (rest === '' || rest === '/') {
    const body = chooserPage(share.files);
    res.writeHead(200, {
      'Content-Type': 'text/html; charset=utf-8',
      'Cache-Control': 'no-store',
    });
    res.end(body);
    return;
  }

  const viewMatch = rest.match(/^\/view\/(\d+)$/);
  if (rest === '/view' || viewMatch) {
    const index = viewMatch ? Number(viewMatch[1]) : 0;
    const file = share.files[index];
    if (!file) {
      res.statusCode = 404;
      res.end();
      return;
    }
    res.writeHead(200, {
      'Content-Type': 'text/html; charset=utf-8',
      'Cache-Control': 'no-store',
    });
    res.end(file.buf);
    return;
  }

  const fileMatch = rest.match(/^\/file\/(\d+)$/);
  if (rest === '/file' || fileMatch) {
    const index = fileMatch ? Number(fileMatch[1]) : 0;
    const file = share.files[index];
    if (!file) {
      res.statusCode = 404;
      res.end();
      return;
    }
    const safeName = file.filename.replace(/[^A-Za-z0-9._-]/g, '_');
    res.writeHead(200, {
      'Content-Type': 'application/octet-stream',
      'Content-Disposition': `attachment; filename="${safeName}"`,
      'Content-Length': String(file.buf.length),
      'Cache-Control': 'no-store',
    });
    res.end(file.buf);
    return;
  }

  res.statusCode = 404;
  res.end();
}

function chooserPage(files: SharedFile[]): string {
  const esc = (value: string) => value.replace(/[&<>"]/g, (c) =>
    ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c] as string)
  );
  const items = files.map((file, index) => {
    const escName = esc(file.filename);
    return `<div class="item">
      <div class="file">${escName}</div>
      <a class="btn view" href="view/${index}">View in browser</a>
      <a class="btn dl" href="file/${index}" download="${escName}">Download .html</a>
    </div>`;
  }).join('');
  return `<!DOCTYPE html>
<html><head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Send To Mobile</title>
<style>
  body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
         margin: 0; padding: 24px; background: #111; color: #eee; }
  h1 { font-size: 18px; font-weight: 500; margin: 0 0 4px; opacity: 0.85; }
  .item { margin-bottom: 26px; }
  .file { font-size: 13px; opacity: 0.6; margin-bottom: 10px; word-break: break-all; }
  a.btn { display: block; padding: 18px; margin-bottom: 14px; border-radius: 10px;
          font-size: 17px; font-weight: 500; text-align: center; text-decoration: none; }
  a.view { background: #2d7dff; color: white; }
  a.dl { background: #2a2a2a; color: #eee; border: 1px solid #3a3a3a; }
</style></head>
<body>
  <h1>HPL Toolbox</h1>
  ${items}
</body></html>`;
}

interface LanIp { address: string; iface: string; }

function getLanIps(): LanIp[] {
  const out: LanIp[] = [];
  const ifaces = os.networkInterfaces();
  for (const [name, list] of Object.entries(ifaces)) {
    if (!list) continue;
    for (const info of list) {
      if (info.family === 'IPv4' && !info.internal) {
        out.push({ address: info.address, iface: name });
      }
    }
  }
  return out;
}

function getPickerDefaultUri(): vscode.Uri | undefined {
  const folders = vscode.workspace.workspaceFolders;
  if (!folders || folders.length === 0) return undefined;
  const root = folders[0].uri.fsPath;
  const dist = path.join(root, 'dist');
  if (fs.existsSync(dist) && fs.statSync(dist).isDirectory()) {
    return vscode.Uri.file(dist);
  }
  return vscode.Uri.file(root);
}

function getHtml(qrScriptUri: string): string {
  return `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<style>
${getToolWebviewStyles()}
  #qr { margin-top: 14px; background: white; padding: 12px; display: inline-block; border-radius: var(--tool-radius); }
  #qr svg, #qr img { display: block; }
  .url {
    margin-top: 10px; padding: 8px;
    background: var(--vscode-textBlockQuote-background);
    border-left: 3px solid var(--vscode-textBlockQuote-border);
    font-family: var(--vscode-editor-font-family, monospace); font-size: 12px; word-break: break-all;
  }
  .url-actions { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; }
  .share-panel { padding: 12px; }
  .iface-radio { text-align: center; }
  #qr { margin-top: 0; }
  .selected-list { margin-top: 10px; }
  .pager { margin-top: 8px; display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
</style>
<script src="${qrScriptUri}"></script>
</head>
<body>
<main class="tool-page">
<header class="tool-header">
  <h2 class="tool-title">Send To Mobile</h2>
  <p class="tool-description">Pick an HTML file, then start sharing. Scan the QR with a phone on the same WiFi. The phone gets View and Download options.</p>
</header>

<section class="tool-panel input-panel">
  <div class="panel-header">
    <h3 class="panel-title">Inputs</h3>
  </div>
  <div class="panel-body">
    <div class="control-group">
      <div class="file-row">
        <button id="pickFolder" class="secondary">Select Folder</button>
        <button id="pick" class="secondary">Select File(s)</button>
        <button id="clear" class="secondary">Clear</button>
      </div>
      <div class="file-name" id="fileName" style="margin-top:8px;">(no files selected)</div>
      <div id="fileList" class="selected-list"></div>
    </div>
    <div class="action-row">
      <button id="start">Start</button>
      <button id="stop" class="secondary" disabled>Stop</button>
    </div>
    <div id="status" class="status-panel"></div>
  </div>
</section>

<div id="outputPanel" class="is-hidden">
    <div id="ifaceWrap" class="control-group" style="display:none;">
      <div class="control-label">Network Interfaces</div>
      <p class="section-description">Pick the interface your phone can reach.</p>
      <div class="iface-list" id="ifaceList"></div>
    </div>

    <div id="shareInfo" class="result-card share-panel" style="display:none;">
      <div id="qr"></div>
      <div class="muted" style="margin-top:8px;font-size:12px;">
        First run on Windows may show a firewall prompt - allow access for "Private networks".
        Sharing stops automatically when this panel is closed.
      </div>
    </div>
</div>
</main>

<script>
  const vscode = acquireVsCodeApi();
  const pickFolderBtn = document.getElementById('pickFolder');
  const pickBtn = document.getElementById('pick');
  const clearBtn = document.getElementById('clear');
  const startBtn = document.getElementById('start');
  const stopBtn = document.getElementById('stop');
  const fileNameEl = document.getElementById('fileName');
  const fileListEl = document.getElementById('fileList');
  const statusEl = document.getElementById('status');
  const outputPanel = document.getElementById('outputPanel');
  const shareInfo = document.getElementById('shareInfo');
  const ifaceWrap = document.getElementById('ifaceWrap');
  const ifaceList = document.getElementById('ifaceList');
  const qrEl = document.getElementById('qr');

  const PAGE_SIZE = 5;
  let currentUrls = [];
  let selectedIdx = 0;
  let selectedPaths = [];
  let selectedFiles = [];
  let selectedPage = 0;

  pickFolderBtn.addEventListener('click', () => vscode.postMessage({ type: 'pickFolder' }));
  pickBtn.addEventListener('click', () => vscode.postMessage({ type: 'pickFile' }));
  clearBtn.addEventListener('click', () => {
    selectedPaths = [];
    selectedFiles = [];
    selectedPage = 0;
    renderSelection();
    statusEl.textContent = '';
    statusEl.classList.remove('is-busy');
  });

  startBtn.addEventListener('click', () => {
    statusEl.innerHTML = '';
    outputPanel.classList.add('is-hidden');
    if (!selectedPaths.length) {
      statusEl.innerHTML = '<span class="err">Pick at least one HTML file first.</span>';
      return;
    }
    startBtn.disabled = true;
    statusEl.textContent = 'Starting server...';
    statusEl.classList.add('is-busy');
    vscode.postMessage({ type: 'startShare', paths: selectedPaths });
  });

  stopBtn.addEventListener('click', () => {
    vscode.postMessage({ type: 'stopShare' });
  });

  function escapeHtml(s) {
    return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  }

  function renderSelection() {
    const oldPager = document.getElementById('selectedPager');
    if (oldPager) oldPager.remove();
    if (!selectedFiles.length) {
      fileNameEl.textContent = '(no files selected)';
      fileListEl.innerHTML = '';
      return;
    }
    fileNameEl.textContent = selectedFiles.length + ' file' + (selectedFiles.length === 1 ? '' : 's');
    const maxPage = Math.max(0, Math.ceil(selectedFiles.length / PAGE_SIZE) - 1);
    selectedPage = Math.min(selectedPage, maxPage);
    const start = selectedPage * PAGE_SIZE;
    const visibleFiles = selectedFiles.slice(start, start + PAGE_SIZE);
    fileListEl.innerHTML =
      '<div class="results-panel" style="margin-top:0;">' +
        '<table class="data-table">' +
          '<thead><tr><th>Filename</th><th style="width:44px;"></th></tr></thead>' +
          '<tbody>' +
            visibleFiles.map((file, offset) => {
              const i = start + offset;
              return (
              '<tr><td class="mono wrap">' + escapeHtml(file.name) + '</td>' +
              '<td><button class="remove-selected danger" data-index="' + i + '" title="Remove">×</button></td></tr>'
              );
            }).join('') +
          '</tbody>' +
        '</table>' +
      '</div>';
    fileListEl.querySelectorAll('.remove-selected').forEach(btn => {
      btn.addEventListener('click', () => {
        const index = Number(btn.dataset.index);
        selectedFiles.splice(index, 1);
        selectedPaths.splice(index, 1);
        renderSelection();
      });
    });
    if (selectedFiles.length > PAGE_SIZE) {
      const pager = document.createElement('div');
      pager.id = 'selectedPager';
      pager.className = 'pager';
      pager.innerHTML =
        '<button class="secondary" id="prevPage"' + (selectedPage === 0 ? ' disabled' : '') + '>Previous</button>' +
        '<span class="file-name">Page ' + (selectedPage + 1) + ' of ' + (maxPage + 1) + '</span>' +
        '<button class="secondary" id="nextPage"' + (selectedPage === maxPage ? ' disabled' : '') + '>Next</button>';
      fileListEl.appendChild(pager);
      pager.querySelector('#prevPage').addEventListener('click', () => { selectedPage--; renderSelection(); });
      pager.querySelector('#nextPage').addEventListener('click', () => { selectedPage++; renderSelection(); });
    }
  }

  renderSelection();

  function renderQr(url) {
    qrEl.innerHTML = '';
    try {
      const qr = qrcode(0, 'M');
      qr.addData(url);
      qr.make();
      qrEl.innerHTML = qr.createSvgTag({ cellSize: 5, margin: 2, scalable: true });
      const svg = qrEl.querySelector('svg');
      if (svg) { svg.setAttribute('width', '240'); svg.setAttribute('height', '240'); }
    } catch (e) {
      qrEl.textContent = 'QR render failed: ' + e.message;
    }
  }

  function renderIfaces(urls) {
    ifaceList.innerHTML = '';
    if (urls.length <= 1) {
      ifaceWrap.style.display = 'none';
      return;
    }
    ifaceWrap.style.display = '';
    ifaceList.innerHTML =
      '<div class="results-panel" style="margin-top:0;">' +
        '<table class="data-table">' +
          '<thead><tr><th style="width:70px;">Use</th><th style="width:140px;">IP</th><th>Interface</th></tr></thead>' +
          '<tbody></tbody>' +
        '</table>' +
      '</div>';
    const tbody = ifaceList.querySelector('tbody');
    urls.forEach((u, i) => {
      const id = 'iface_' + i;
      const row = document.createElement('tr');
      row.innerHTML =
        '<td class="iface-radio"><input type="radio" name="iface" id="' + id + '"' + (i === selectedIdx ? ' checked' : '') + ' /></td>' +
        '<td class="mono wrap">' + u.ip + '</td>' +
        '<td class="wrap">' + u.iface + '</td>';
      row.querySelector('input').addEventListener('change', () => {
        selectedIdx = i;
        renderQr(urls[i].url);
      });
      tbody.appendChild(row);
    });
  }

  window.addEventListener('message', (e) => {
    const m = e.data;
    if (m.type === 'fileSelected') {
      const existing = new Set(selectedFiles.map(f => f.path));
      selectedFiles = selectedFiles.concat((m.files || []).filter(f => !existing.has(f.path)));
      selectedPaths = selectedFiles.map(f => f.path);
      selectedPage = 0;
      renderSelection();
    } else if (m.type === 'status') {
      statusEl.textContent = m.message;
      statusEl.className = 'status-panel is-busy';
    } else if (m.type === 'sharing') {
      statusEl.classList.remove('is-busy');
      currentUrls = m.urls;
      selectedIdx = 0;
      statusEl.innerHTML = '<span class="ok">Sharing: ' + m.filename + '</span>';
      outputPanel.classList.remove('is-hidden');
      shareInfo.style.display = '';
      renderIfaces(m.urls);
      renderQr(m.urls[0].url);
      startBtn.disabled = true;
      stopBtn.disabled = false;
    } else if (m.type === 'stopped') {
      statusEl.classList.remove('is-busy');
      statusEl.textContent = 'Stopped.';
      shareInfo.style.display = 'none';
      ifaceWrap.style.display = 'none';
      outputPanel.classList.add('is-hidden');
      currentUrls = [];
      startBtn.disabled = false;
      stopBtn.disabled = true;
    } else if (m.type === 'error') {
      statusEl.classList.remove('is-busy');
      statusEl.innerHTML = '<span class="err">' + (m.message || 'Error').replace(/</g, '&lt;') + '</span>';
      startBtn.disabled = false;
    }
  });
</script>
</body>
</html>`;
}
