import { access, constants, readFile, stat } from 'node:fs';
import express from 'express';
import path from 'node:path';
import { fileURLToPath } from 'url';
import { bufferFile, wc, head, escape } from './utils.js';
const app = express()
const port = 3000
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

app.engine('cosmic', function (filePath, options, callback) {
  readFile(filePath, function (err, content) {
    var s
    if (err) return callback(err)
    var rendered = content.toString()
    for (s in options) {
      if (typeof options[s] === 'string') {
        rendered = rendered.replace('##' + s + '##', options[s])
      }
    }
    return callback(null, rendered)
  })
})

app.set('views', './views')
app.set('view engine', 'cosmic')

app.get('/', async function (_req, res) {
  const intro = bufferFile('/var/gopher/intro.gophermap')
  const recent = await head('/var/gopher/listing.gophermap', 20)
  var lines = await wc('/var/gopher/listing.gophermap')
  var content = ''
  for (let i = 0; i < recent.length; ++i) {
    const split = recent[i].split('\t')
    let link = split[1]
    link = link.replace(/\.txt$/, '.html')
    const name = split[0].substr(1)
    content += '<a href="' + link + '">' + lines + ' <span class="dim">&gt;&gt;</span> ' + escape(name) + '</a>\n'
    lines--
  }
  res.render('index', { intro: intro, recent: content })
})

app.get('/log', async function (req, res) {
  const list = await head('/var/gopher/listing.gophermap')
  var lines = await wc('/var/gopher/listing.gophermap')
  var content = ''
  for (let i = 0; i < list.length; ++i) {
    const split = list[i].split('\t')
    let link = split[1]
    link = link.replace(/\.txt$/, '.html')
    const name = split[0].substr(1)
    content += '<a href="' + link + '">' + String(lines).padStart(3, '0') + ' <span class="dim">&gt;&gt;</span> ' + escape(name) + '</a>\n'
    lines--
  }
  const back = '<a href="/"><span class="dim">&lt;&lt;</span> BACK TO COSMIC VOYAGE</a>'
  const label = 'RS001 Log Entries (Newest First):'
  const compiled = back + '\n\n' + label + '\n\n' + content
  const fullUrl = 'https://cosmic.voyage' + req.originalUrl
  res.render('basic', { content: compiled, canonical: fullUrl})
})

app.get('/ships', async function (_req, res) {
  const intro = bufferFile('/var/gopher/ships/ships.gophermap')
  const list = await head('/var/gopher/listing.gophermap')
  const ships = {}
  for (let i = 0; i < list.length; ++i) {
    const split = list[i].split('\t')
    let name = split[0].substr(1)
    name = name.split(' - ')[0].trim()
    ships[name] = ships[name] + 1 || 1
  }
  var keys = Object.keys(ships);
  keys.sort();
  var content = ''
  for(let i = 0; i < keys.length; ++i){
    content += '<span class="dim">&gt;&gt;</span> <a href="/ships/' + encodeURIComponent(keys[i]) + '">' + keys[i] + ' (' + ships[keys[i]] + ' logs)</a>\n'
  }
  res.render('ships', { intro: intro, ships: content })
})

app.get('/ships/*', async function (req, res) {
  const list = await head('/var/gopher/listing.gophermap')
  const ship = decodeURIComponent(req.path).replace(new RegExp('/ships/', 'i'), '').replace(new RegExp('/(?:index.html)?$', 'i'), '');
  const description = escape(bufferFile('/var/gopher/' + ship + '/.description')) || ''
  const license = bufferFile('/var/gopher/' + ship + '/LICENSE') || ''
  var licenseLabel = ''
  var licenseContent = ''
  if (license) {
    const l = await head('/var/gopher/' + ship + '/LICENSE', 1)
    licenseLabel = l[0]
    licenseContent = '<a href="/' + encodeURIComponent(ship) + '/LICENSE">' + licenseLabel + '</a>\n'
  } else {
    licenseContent = 'All rights reserved.'
  }
  var content = ship + ' - Ship Log:\n'
  for (let i = list.length - 1; i >= 0; --i) {
    if (list[i].startsWith('0' + ship)) {
      const split = list[i].split('\t')
      let link = split[1]
      link = link.replace(/\.txt$/, '.html')
      const name = split[0].substr(1)
      content += '<a href="' + link + '">' + String(list.length - i).padStart(3, '0') + ' <span class="dim">&gt;&gt;</span> ' + escape(name) + '</a>\n'
    }
  }
  const fullUrl = 'https://cosmic.voyage' + req.originalUrl
  res.render('ship-detail', { description: description, content: content, license: licenseContent, canonical: fullUrl })
})

app.get('/rss.xml', function (_req, res) {
  var content = bufferFile('/var/gopher/rss.xml')
  const domainRegexp = new RegExp('gopher://cosmic.voyage(/./)?', 'g')
  const extensionRegexp = new RegExp('\.txt', 'g')
  content = content.replace(domainRegexp, 'https://cosmic.voyage/')
  content = content.replace(extensionRegexp, '.html')
  res.setHeader('content-type', 'text/xml')
  res.render('raw', { content: content })
})

app.get('/atom.xml', function (_req, res) {
  var content = bufferFile('/var/gopher/atom.xml')
  const domainRegexp = new RegExp('gopher://cosmic.voyage/(0/)?', 'g')
  const extensionRegexp = new RegExp('\.txt', 'g')
  content = content.replace(domainRegexp, 'https://cosmic.voyage/')
  content = content.replace(extensionRegexp, '.html')
  res.setHeader('content-type', 'text/xml')
  res.render('raw', { content: content })
})

app.get('/sitemap.xml', function (_req, res) {
  var content = bufferFile('/var/gopher/sitemap.xml')
  stat('/var/gopher/rss.xml', (err, stats) => {
    if (err) {
      res(err)
    } else {
      content = content.replace(/##date##/g, stats.mtime.toISOString())
      res.setHeader('content-type', 'text/xml')
      res.render('raw', { content: content })
    }
  })
})

app.get('/.well-known/webfinger', function(req, res) {
  var resource = req.query.resource
  if (resource && resource.startsWith('acct:') && resource.endsWith('@cosmic.voyage')) {
    const regex = /acct:(\w+)@cosmic\.voyage/i
    const resources = resource.match(regex)
    if (resources.length) {
      const user = resources[1].toLowerCase()
      const path = '/home/' + user + '/.webfinger.json'
      access(path, constants.R_OK, (err) => {
        if (err) {
          res.setHeader('content-type', 'application/jrd+json')
          res.render('raw', { content: '' })
        } else {
          const content = bufferFile(path)
          res.setHeader('content-type', 'application/jrd+json')
          res.render('raw', { content: content })
        }
      })
    } else {
      res.setHeader('content-type', 'application/jrd+json')
      res.render('raw', { content: '' })
    }
  } else {
    res.setHeader('content-type', 'application/jrd+json')
    res.render('raw', { content: '' })
  }
})

// Any link to a direct static resource will show it
app.use(express.static(path.join(__dirname, '/static')))

// Override default LICENSE display and format for cosmic styles
app.get('*/LICENSE', function(req, res){
  var file = path.join('/var/gopher/', decodeURIComponent(req.path));
  access(file, constants.R_OK, (err) => {
    if (err) {
      const back = '<a href="/"><span class="dim">&lt;&lt;</span> BACK TO COSMIC VOYAGE</a>'
      const error = 'Message not found. Please try again.'
      const content = back + '\n\n' + error
      res.status(404)
      const fullUrl = 'https://cosmic.voyage' + req.originalUrl
      res.render('basic', { content: content, canonical: fullUrl})
    } else {
      const file = escape(bufferFile('/var/gopher/' + decodeURIComponent(req.path)))
      const back = '<a href="/log"><span class="dim">&lt;&lt;</span> BACK TO RS001 LOG</a>'
      const content = back + '\n\n' + file
      res.setHeader('content-type', 'text/html')
      const fullUrl = 'https://cosmic.voyage' + req.originalUrl
      res.render('basic', { content: content, canonical: fullUrl})
    }
  })
})

// Any other gopher content directly linked will show as-is
app.use(express.static('/var/gopher'))

// Grab anything that's a .txt and wrap it in .html
app.get('*', function(req, res){
  if (req.path.indexOf('.html') !== -1) {
    const file = path.join('/var/gopher/', decodeURIComponent(req.path).replace(/\.html/, '.txt'));
    access(file, constants.R_OK, (err) => {
      if (err) {
        const back = '<a href="/"><span class="dim">&lt;&lt;</span> BACK TO COSMIC VOYAGE</a>'
        const error = 'Message not found. Please try again.'
        const content = back + '\n\n' + error
        res.status(404)
        const fullUrl = 'https://cosmic.voyage' + req.originalUrl
        res.render('basic', { content: content, canonical: fullUrl})
      } else {
        const file = escape(bufferFile('/var/gopher/' + decodeURIComponent(req.path).replace(/\.html/, '.txt')))
        const back = '<a href="/log"><span class="dim">&lt;&lt;</span> BACK TO RS001 LOG</a>'
        const content = back + '\n\n' + file
        const fullUrl = 'https://cosmic.voyage' + req.originalUrl
        res.render('basic', { content: content, canonical: fullUrl})
      }
    })
  } else {
    const back = '<a href="/"><span class="dim">&lt;&lt;</span> BACK TO COSMIC VOYAGE</a>'
    const error = 'Message not found. Please try again.'
    const content = back + '\n\n' + error
    res.status(404)
    const fullUrl = 'https://cosmic.voyage' + req.originalUrl
    res.render('basic', { content: content, canonical: fullUrl})
  }
})

app.listen(port, () => console.log(`listening on port ${port}!`))
