const http = require('http')
const fs = require('fs')
const path = require('path')
const crypto = require('crypto')
const { spawnSync } = require('child_process')

const PORT = Number(process.env.PORT || 8787)
const EMAIL_GEN_ROOT = path.resolve(process.env.EMAIL_GEN_ROOT || '/workspace/email-gen')

function send(res, status, payload) {
  res.statusCode = status
  res.setHeader('Content-Type', 'application/json')
  res.end(JSON.stringify(payload))
}

const MAX_BODY_SIZE = 5 * 1024 * 1024

function readBody(req) {
  return new Promise((resolve) => {
    let size = 0
    let data = ''
    req.on('data', (chunk) => {
      size += chunk.length
      if (size <= MAX_BODY_SIZE) data += chunk
    })
    req.on('end', () => {
      if (size > MAX_BODY_SIZE) return resolve({})
      try {
        resolve(data ? JSON.parse(data) : {})
      } catch {
        resolve({})
      }
    })
    req.on('error', () => resolve({}))
  })
}

function sanitizeProjectSlug(value) {
  return String(value || '').trim().replace(/[^a-zA-Z0-9_-]/g, '')
}

function validatePugSafety(pug) {
  const dangerous = [
    /\brequire\s*\(/i,
    /\bprocess\b/,
    /\bchild_process\b/,
    /\bexecSync\b/,
    /\bexec\s*\(/,
    /\bspawn\s*\(/,
    /\beval\s*\(/,
    /\bFunction\s*\(/,
    /\bnew\s+Function\b/,
    /\bglobal\b/,
    /\b__dirname\b/,
    /\b__filename\b/,
    /\bfs\s*\./,
    /\bBuffer\b/,
  ]
  for (const re of dangerous) {
    if (re.test(pug)) {
      return { safe: false, pattern: re.source }
    }
  }
  return { safe: true }
}

function ensureBuilderPreviewTemplate(projectDir) {
  const templateName = '__builder_preview__.pug'
  const templatePath = path.resolve(projectDir, templateName)
  const templateBody = [
    'extends ./html.pug',
    '',
    'block content',
    '  include ./letters/let.pug',
    '',
  ].join('\n')
  fs.writeFileSync(templatePath, templateBody, 'utf-8')
  return { templateName, templatePath }
}

function ensureNpmDeps() {
  const marker = path.resolve(EMAIL_GEN_ROOT, 'node_modules')
  if (fs.existsSync(marker)) return
  const install = spawnSync('npm', ['install'], {
    cwd: EMAIL_GEN_ROOT,
    encoding: 'utf-8',
    shell: false,
  })
  if (install.status !== 0) {
    throw new Error((install.stderr || install.stdout || 'npm install failed').trim())
  }
}

function renderWithNode(projectSlug, renderTemplate = 'html') {
  const outFileName = `render_${crypto.randomBytes(16).toString('hex')}.html`
  const script = `
const path = require('path');
const fs = require('fs');
const Email = require('email-templates');

async function run() {
  const project = process.argv[1];
  const renderTemplate = process.argv[2];
  const root = process.argv[3];
  const outFile = process.argv[4];
  const email = new Email();
  const html = await email.render({
    path: project + '/' + renderTemplate,
    juiceResources: {
      preserveImportant: true,
      applyStyleTags: true,
      removeStyleTags: true,
      preserveMediaQueries: true,
      webResources: {
        relativeTo: path.resolve(root, 'emails', project)
      }
    },
  }, { pretty: true });

  const outDir = path.resolve(root, 'public');
  if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
  const outPath = path.resolve(outDir, outFile);
  const normalized = String(html).replace('#MAILRU_PREHEADER_TAG#', '<vk-snippet-end/>');
  fs.writeFileSync(outPath, normalized, 'utf-8');
}

run().catch((error) => {
  console.error(error && error.stack ? error.stack : String(error));
  process.exit(1);
});
`

  const run = spawnSync(process.execPath, ['-e', script, projectSlug, renderTemplate, EMAIL_GEN_ROOT, outFileName], {
    cwd: EMAIL_GEN_ROOT,
    encoding: 'utf-8',
    shell: false,
    timeout: 120000,
  })

  if (run.error || run.status !== 0) {
    throw new Error((run.stderr || run.stdout || run.error?.message || 'render failed').trim())
  }

  return outFileName
}

function applyNowrap(html) {
  const templatePlaceholders = []
  let protected_ = html.replace(/@\{[^}]*\}/g, (m) => {
    templatePlaceholders.push(m)
    return `\u200BTPL${templatePlaceholders.length - 1}\u200B`
  })

  function wrapShort(text) {
    return text.replace(
      /(?<![a-zA-Zа-яА-ЯёЁ])([a-zA-Zа-яА-ЯёЁ]{1,3})(?:\s|&nbsp;)+(\S+)/gu,
      '\u200Bspan\u200Bnwr\u200B$1\u00A0$2\u200B/span\u200Bnwr\u200B'
    )
  }
  const result = protected_.replace(
    /(<span[^>]*class="[^"]*\bh3\b[^"]*"[^>]*>)([\s\S]*?)(<\/span><\/td>)/gi,
    (_match, open, content, close) => {
      const processed = content.replace(/>([^<]+)</g, (m, t) => '>' + wrapShort(t) + '<')
      const firstText = processed.replace(/^([^<]+)/, (m) => wrapShort(m))
      return open + firstText + close
    }
  )
  return result
    .replace(/\u200Bspan\u200Bnwr\u200B/g, '<span style="white-space:nowrap">')
    .replace(/\u200B\/span\u200Bnwr\u200B/g, '</span>')
    .replace(/\u200BTPL(\d+)\u200B/g, (_, i) => templatePlaceholders[parseInt(i)])
}

const server = http.createServer(async (req, res) => {
  if (req.method === 'GET' && req.url === '/health') {
    return send(res, 200, { ok: true })
  }

  if (req.method === 'POST' && req.url === '/render') {
    const body = await readBody(req)
    const projectSlug = sanitizeProjectSlug(body.projectSlug)
    const pug = String(body.pug || '')

    if (!projectSlug) return send(res, 400, { error: 'missing_project_slug', details: 'Project slug required' })
    if (!pug.trim()) return send(res, 400, { error: 'missing_pug', details: 'PUG required' })

    if (!fs.existsSync(EMAIL_GEN_ROOT)) {
      return send(res, 500, { error: 'email_gen_not_found', details: 'email-gen root missing' })
    }

    const emailsRoot = path.resolve(EMAIL_GEN_ROOT, 'emails')
    const projectDir = path.resolve(emailsRoot, projectSlug)
    if (!projectDir.startsWith(emailsRoot)) {
      return send(res, 400, { error: 'invalid_project_slug', details: 'Invalid project slug' })
    }
    if (!fs.existsSync(projectDir)) {
      return send(res, 404, { error: 'email_project_not_found', details: `Project ${projectSlug} not found` })
    }

    const pugCheck = validatePugSafety(pug)
    if (!pugCheck.safe) {
      return send(res, 400, { error: 'unsafe_pug', details: `Dangerous pattern detected: ${pugCheck.pattern}` })
    }

    try {
      ensureNpmDeps()
      const lettersDir = path.resolve(projectDir, 'letters')
      if (!fs.existsSync(lettersDir)) fs.mkdirSync(lettersDir, { recursive: true })
      fs.writeFileSync(path.resolve(lettersDir, 'let.pug'), pug, 'utf-8')
      const { templateName, templatePath } = ensureBuilderPreviewTemplate(projectDir)
      let outFileName
      try {
        outFileName = renderWithNode(projectSlug, templateName.replace(/\.pug$/, ''))
      } finally {
        if (fs.existsSync(templatePath)) fs.unlinkSync(templatePath)
      }

      const htmlPath = path.resolve(EMAIL_GEN_ROOT, 'public', outFileName)
      if (!fs.existsSync(htmlPath)) {
        return send(res, 500, { error: 'preview_not_found', details: 'Rendered file not found' })
      }

      const rawHtml = fs.readFileSync(htmlPath, 'utf-8')
      try { fs.unlinkSync(htmlPath) } catch (_) {}
      const html = applyNowrap(rawHtml)
      return send(res, 200, {
        html,
        generatedAt: new Date().toISOString(),
      })
    } catch (error) {
      return send(res, 500, {
        error: 'render_failed',
        details: error?.message || 'Unknown render error',
      })
    }
  }

  return send(res, 404, { error: 'not_found' })
})

server.listen(PORT, () => {
  console.log(`email-gen-api listening on :${PORT}`)
  console.log(`email-gen root: ${EMAIL_GEN_ROOT}`)
})
