#!/usr/bin/env node
/**
 * Production static server for Laputa App demo.
 * Serves dist/ + handles /api/vault/* routes for browser testing.
 */

import http from 'http'
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import matter from 'gray-matter'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
const DIST_DIR = path.join(__dirname, '..', 'dist')
const REPO_DIR = path.resolve(__dirname, '..')
const PORT = 5173

function isAllowedPath(p) {
  const resolved = path.resolve(p)
  return resolved.startsWith(REPO_DIR)
}

const MIME = {
  '.html': 'text/html',
  '.js':   'application/javascript',
  '.css':  'text/css',
  '.svg':  'image/svg+xml',
  '.png':  'image/png',
  '.ico':  'image/x-icon',
  '.woff2':'font/woff2',
  '.json': 'application/json',
}

function findMarkdownFiles(dir) {
  const results = []
  try {
    for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
      const full = path.join(dir, entry.name)
      if (entry.isDirectory()) results.push(...findMarkdownFiles(full))
      else if (entry.name.endsWith('.md')) results.push(full)
    }
  } catch {}
  return results
}

function extractWikiLinks(value) {
  if (!value) return []
  const str = Array.isArray(value) ? value.join(' ') : String(value)
  return [...str.matchAll(/\[\[([^\]]+)\]\]/g)].map(m => `[[${m[1]}]]`)
}

function parseMarkdownFile(filePath) {
  try {
    const raw = fs.readFileSync(filePath, 'utf-8')
    const { data: fm, content } = matter(raw)
    const stat = fs.statSync(filePath)

    const DEDICATED = new Set(['aliases','Is A','Belongs to','Related to','Status','Owner','Cadence','Created at'])
    const relationships = {}
    for (const [k, v] of Object.entries(fm)) {
      if (DEDICATED.has(k)) continue
      const links = extractWikiLinks(v)
      if (links.length) relationships[k] = links
    }

    const bodyText = content.replace(/---[\s\S]*?---/, '').trim()
    const h1 = bodyText.match(/^#\s+(.+)/m)?.[1]
    const aliases = Array.isArray(fm.aliases) ? fm.aliases : fm.aliases ? [fm.aliases] : []

    return {
      path: filePath,
      filename: path.basename(filePath),
      title: h1 || aliases[0] || path.basename(filePath, '.md'),
      isA: fm['Is A'] ?? null,
      aliases,
      belongsTo: extractWikiLinks(fm['Belongs to']),
      relatedTo: extractWikiLinks(fm['Related to']),
      status: fm['Status'] ?? null,
      owner: fm['Owner'] ?? null,
      cadence: fm['Cadence'] ?? null,
      modifiedAt: stat.mtimeMs,
      createdAt: fm['Created at'] ? new Date(fm['Created at']).getTime() : null,
      fileSize: stat.size,
      snippet: bodyText.replace(/^#+\s+.+/gm, '').replace(/\n+/g, ' ').trim().slice(0, 200),
      relationships,
    }
  } catch { return null }
}

function serveVaultApi(url, res) {
  const params = new URL(url, 'http://localhost')

  if (params.pathname === '/api/vault/ping') {
    res.writeHead(200, { 'Content-Type': 'application/json' })
    res.end(JSON.stringify({ ok: true }))
    return true
  }

  if (params.pathname === '/api/vault/list') {
    const dir = params.searchParams.get('path')
    if (!dir || !isAllowedPath(dir) || !fs.existsSync(dir)) {
      res.writeHead(400); res.end(JSON.stringify({ error: 'bad path' })); return true
    }
    const entries = findMarkdownFiles(dir).map(parseMarkdownFile).filter(Boolean)
    res.writeHead(200, { 'Content-Type': 'application/json' })
    res.end(JSON.stringify(entries))
    return true
  }

  if (params.pathname === '/api/vault/content') {
    const file = params.searchParams.get('path')
    if (!file || !isAllowedPath(file) || !fs.existsSync(file)) {
      res.writeHead(400); res.end(JSON.stringify({ error: 'bad path' })); return true
    }
    res.writeHead(200, { 'Content-Type': 'application/json' })
    res.end(JSON.stringify({ content: fs.readFileSync(file, 'utf-8') }))
    return true
  }

  if (params.pathname === '/api/vault/all-content') {
    const dir = params.searchParams.get('path')
    if (!dir || !isAllowedPath(dir) || !fs.existsSync(dir)) {
      res.writeHead(400); res.end(JSON.stringify({ error: 'bad path' })); return true
    }
    const map = {}
    for (const f of findMarkdownFiles(dir)) {
      try { map[f] = fs.readFileSync(f, 'utf-8') } catch {}
    }
    res.writeHead(200, { 'Content-Type': 'application/json' })
    res.end(JSON.stringify(map))
    return true
  }

  return false
}

const server = http.createServer((req, res) => {
  const url = req.url ?? '/'

  // API routes
  if (url.startsWith('/api/vault/')) {
    if (!serveVaultApi(url, res)) {
      res.writeHead(404); res.end()
    }
    return
  }

  // Static files
  let filePath = path.join(DIST_DIR, url === '/' ? 'index.html' : url)
  if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
    filePath = path.join(DIST_DIR, 'index.html') // SPA fallback
  }
  const ext = path.extname(filePath)
  res.writeHead(200, { 'Content-Type': MIME[ext] ?? 'application/octet-stream' })
  fs.createReadStream(filePath).pipe(res)
})

server.listen(PORT, '0.0.0.0', () => {
  console.log(`✅ Laputa demo server running on http://0.0.0.0:${PORT}`)
  console.log(`   Tailscale: https://mac-mini.tail7cbc15.ts.net`)
})
