const { getCache } = require('./cache');
const { listen } = require('../../../lib/db/notify');
const channels = require('../../../lib/db/channels');

const rels = [
	{
		channels: [ "realtime" ],
		urls: [ ]
	},
	{
		channels: [ "event" ],
		urls: [ /^\/project\/([^\/]+)\/event/ ],
		matches: [ "project" ]
	},
	{
		channels: [ "project" ],
		urls: [ /^\/project\/([^\/]+)\// ],
		matches: [ "project" ]
	},
	{
		channels: [ "preplot_lines", "preplot_points" ],
		urls: [ /^\/project\/([^\/]+)\/line[\/?]?/ ],
		matches: [ "project" ]
	},
	{
		channels: [ "planned_lines" ],
		urls: [ /^\/project\/([^\/]+)\/plan[\/?]?/ ],
		matches: [ "project" ]
	},
	{
		channels: [ "raw_lines", "raw_shots" ],
		urls: [ /^\/project\/([^\/]+)\/sequence[\/?]?/ ],
		matches: [ "project" ]
	},
	{
		channels: [ "final_lines", "final_shots" ],
		urls: [ /^\/project\/([^\/]+)\/sequence[\/?]?/ ],
		matches: [ "project" ]
	},
	{
		channels: [ "info" ],
		urls: [ ],
		matches: [ ],
		callback (url, data) {
			if (data.payload?.table == "info") {
				const pid = data.payload?.pid;
				const key = (data.payload?.new ?? data.payload?.old)?.key;

				const rx = /^\/project\/([^\/]+)\/info\/([^\/?]+)[\/?]?/;
				const match = url.match(rx);
				if (match) {
					if (match[1] == pid) {
						if (match[2] == data.payload?.old?.key || match[2] == data.payload?.new?.key) {
							return true;
						}
					}
				}

				if (key == "plan") {
					const rx = /^\/project\/([^\/]+)\/plan[\/?]?/;
					const match = url.match(rx);
					if (match) {
						return match[1] == pid;
					}
				}
			}
			return false;
		}
	},
	{
		channels: [ "queue_items" ],
		urls: [ ]
	},
]

function invalidateCache (data, cache) {
	return new Promise((resolve, reject) => {
		const channel = data.channel;
		const project = data.payload.pid;
		const operation = data.payload.operation;
		const table = data.payload.table;
		const fields = { channel, project, operation, table };

		for (let rel of rels) {
			if (rel.channels.includes(channel)) {
				for (let url of rel.urls) {
					for (let [key, data] of Object.entries(cache)) {
						const match = key.match(url)?.slice(1);
						if (match) {
							if (rel.matches) {
								if (rel.matches.every( (field, idx) => match[idx] == fields[field] )) {
									console.log("DELETE ENTRY (MATCHES)", key);
									delete cache[key];
								}
							} else {
								// Delete unconditionally
								console.log("DELETE ENTRY (UNCONDITIONAL)", key);
								delete cache[key];
							}
						}

					}
				}

				if (rel.callback) {
					for (let key of Object.keys(cache)) {
						if (rel.callback(key, data)) {
							console.log("DELETE ENTRY (CALLBACK)", key);
							delete cache[key];
						}
					}
				}

			}
		}

		resolve();
	});
}

async function watch (app) {
	if (!app.locals?.etags) {
		app.locals.etags = {};
	}

	function etagWatch (data) {
		invalidateCache(data, app.locals.etags);
	}
	try {
		const client = await listen(channels, etagWatch);
		console.log("ETag watch installed", client);
	} catch (err) {
		console.error(err);
		console.log("ETag watch not installed");
	}
}

module.exports = watch;
