import escapeRegexp from 'escape-regexp';
import config from '@/config/index.js';
import { Note } from '@/models/entities/note.js';
import { CacheableUser } from '@/models/entities/user.js';
import { MessagingMessage } from '@/models/entities/messaging-message.js';
import { Notes, MessagingMessages } from '@/models/index.js';
import { uriPersonCache, userByIdCache } from '@/services/user-cache.js';
import { IObject, getApId } from './type.js';

export type UriParseResult = {
	/** wether the URI was generated by us */
	local: true;
	/** id in DB */
	id: string;
	/** hint of type, e.g. "notes", "users" */
	type: string;
	/** any remaining text after type and id, not including the slash after id. undefined if empty */
	rest?: string;
} | {
	/** wether the URI was generated by us */
	local: false;
	/** uri in DB */
	uri: string;
};

export function parseUri(value: string | IObject): UriParseResult {
	const uri = getApId(value);

	// the host part of a URL is case insensitive, so use the 'i' flag.
	const localRegex = new RegExp('^' + escapeRegexp(config.url) + '/(\\w+)/(\\w+)(?:\/(.+))?', 'i');
	const matchLocal = uri.match(localRegex);

	if (matchLocal) {
		return {
			local: true,
			type: matchLocal[1],
			id: matchLocal[2],
			rest: matchLocal[3],
		};
	} else {
		return {
			local: false,
			uri,
		};
	}
}

export class DbResolver {
	constructor() {
	}

	/**
	 * AP Note => FoundKey Note in DB
	 */
	public async getNoteFromApId(value: string | IObject): Promise<Note | null> {
		const parsed = parseUri(value);

		if (parsed.local) {
			if (parsed.type !== 'notes') return null;

			return await Notes.findOneBy({
				id: parsed.id,
			});
		} else {
			return await Notes.findOneBy([{
				uri: parsed.uri,
			}, {
				url: parsed.uri,
			}]);
		}
	}

	public async getMessageFromApId(value: string | IObject): Promise<MessagingMessage | null> {
		const parsed = parseUri(value);

		if (parsed.local) {
			if (parsed.type !== 'notes') return null;

			return await MessagingMessages.findOneBy({
				id: parsed.id,
			});
		} else {
			return await MessagingMessages.findOneBy({
				uri: parsed.uri,
			});
		}
	}

	/**
	 * AP Person => FoundKey User in DB
	 */
	public async getUserFromApId(value: string | IObject): Promise<CacheableUser | null> {
		const parsed = parseUri(value);

		if (parsed.local) {
			if (parsed.type !== 'users') return null;

			return await userByIdCache.fetch(parsed.id) ?? null;
		} else {
			return await uriPersonCache.fetch(parsed.uri) ?? null;
		}
	}
}
