const sqlite3 = require('better-sqlite3')

const db = sqlite3(`./data/log.sqlite`)
db.pragma('journal_mode = DELETE')
db.pragma('synchronous = 0')

db.exec(`
	create table if not exists http (time integer default (strftime('%s','now')), method text, url text, headers text, body text, status text, duration integer);	
	create table if not exists  mtproto (time integer default (strftime('%s','now')), message text);
	create table if not exists  error (time integer default (strftime('%s','now')), message text, stack text);
`)

function http (req, res, duration) {
	const data = {
		method: req.method,
		url: req.url,
		status: res.statusCode,
		duration
	};

	['headers', 'body'].forEach(key => {
		const value = req.body[key] 
		try {
			data[key] = value instanceof Array && value.length < 1_000_000 || value instanceof Object ? JSON.stringify(value) : value?.toString() || null 
		} catch (err) {
			error(err)
			data[key] = value?.toString() || null
		} 
	})
	
	db
		.prepare(`insert into http (method, url, headers, body, status, duration) values (:method, :url, :headers, :body, :status, :duration)`)
		.run(data)	
}

function safeStringify(obj, indent = 2) {
	const cache = new Set()

	return JSON.stringify(obj, (key, value) => {
		if (typeof value === 'object' && value !== null) {
			if (cache.has(value)) 
				return '[Circular]'

			cache.add(value)
		}

		return value
	}, indent)
}

function mtproto (update) {
	const message = update
	message.self = message
	
	db
		.prepare(`insert into mtproto (message) values (:message)`)
		.run({ 
			message: safeStringify(message) 
		})
}

function error (...args) {
	const stacks = []
	const message = args.map(arg => {
		if (arg instanceof Error) {
			stacks.push(arg.stack)
			return 'Error: ' + arg.message
		} else if (arg instanceof Object) {
			try {
				return JSON.stringify(arg)
			} catch (err) { }
		}

		return String(arg)
	}).join('\n')

	if (stacks.length == 0)
		stacks.push(new Error().stack)

	console.error(message)

	db
		.prepare(`insert into error (message, stack) values (:message, :stack)`)
		.run({ 
			message, 
			stack: stacks.join('\n\n') 
		})	
}

module.exports = { http, mtproto, error }