import config from "../../config/index.js";
import { Notes, Users, UserPublickeys, MessagingMessages } from "../../models/index.js";
import { Cache } from "../../misc/cache.js";
import { uriPersonCache, userByIdCache } from "../../services/user-cache.js";
import { getApId } from "./type.js";
import { resolvePerson, updatePerson } from "./models/person.js";
import { subscriber } from "../../db/redis.js";
import { toPuny } from "../../misc/convert-host.js";
const publicKeyCache = new Cache("publicKey", 60 * 30);
const publicKeyByUserIdCache = new Cache("publicKeyByUserId", 60 * 30);
export function parseUri(value) {
    const uri = getApId(value);
    const parsed = new URL(uri);
    if (toPuny(parsed.host) === toPuny(config.host)) {
        const localRegex = new RegExp(`^.*?/(\\w+)/(\\w+)(?:/(.+))?`);
        const matchLocal = uri.match(localRegex);
        if (matchLocal == null) {
            throw new Error(`Failed to parse local URI: ${uri}`);
        }
        return {
            local: true,
            type: matchLocal[1],
            id: matchLocal[2],
            rest: matchLocal[3]
        };
    } else {
        return {
            local: false,
            uri
        };
    }
}
export default class DbResolver {
    /**
	 * AP Note => Misskey Note in DB
	 */ async getNoteFromApId(value) {
        const parsed = parseUri(value);
        if (parsed.local) {
            if (parsed.type !== "notes") return null;
            return await Notes.findOneBy({
                id: parsed.id
            });
        } else {
            return await Notes.findOne({
                where: [
                    {
                        uri: parsed.uri
                    },
                    {
                        url: parsed.uri
                    }
                ]
            });
        }
    }
    async getMessageFromApId(value) {
        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 => Misskey User in DB
	 */ async getUserFromApId(value) {
        const parsed = parseUri(value);
        if (parsed.local) {
            if (parsed.type !== "users") return null;
            return await userByIdCache.fetchMaybe(parsed.id, ()=>Users.findOneBy({
                    id: parsed.id
                }).then((x)=>x ?? undefined), true) ?? null;
        } else {
            return await uriPersonCache.fetch(parsed.uri, ()=>Users.findOneBy({
                    uri: parsed.uri
                }), true);
        }
    }
    /**
	 * AP KeyId => Misskey User and Key
	 */ async getAuthUserFromKeyId(keyId) {
        const key = await publicKeyCache.fetch(keyId, async ()=>{
            const key = await UserPublickeys.findOneBy({
                keyId
            });
            if (key == null) return null;
            return key;
        }, true, (key)=>key != null);
        if (key == null) return null;
        return {
            user: await userByIdCache.fetch(key.userId, ()=>Users.findOneByOrFail({
                    id: key.userId
                }), true),
            key
        };
    }
    /**
	 * AP Actor id => Misskey User and Key
	 */ async getAuthUserFromApId(uri) {
        const user = await resolvePerson(uri);
        if (user == null) return null;
        const key = await publicKeyByUserIdCache.fetch(user.id, ()=>UserPublickeys.findOneBy({
                userId: user.id
            }), true, (v)=>v != null);
        return {
            user,
            key
        };
    }
    async refetchPublicKeyForApId(user) {
        try {
            await updatePerson(user.uri, undefined, undefined, user);
            let key = await UserPublickeys.findOneBy({
                userId: user.id
            });
            if (key != null) {
                await publicKeyByUserIdCache.set(user.id, key);
            }
            return key;
        } catch  {
            return null;
        }
    }
}
subscriber.on("message", async (_, data)=>{
    const obj = JSON.parse(data);
    if (obj.channel === "internal") {
        const { type, body } = obj.message;
        switch(type){
            case "remoteUserDeleted":
            case "localUserDeleted":
                {
                    const toDelete = Array.from(await publicKeyByUserIdCache.getAll()).filter((v)=>v[1]?.userId === body.id).map((v)=>v[0]);
                    const toDeleteKey = Array.from(await publicKeyCache.getAll()).filter((v)=>v[1]?.userId === body.id).map((v)=>v[0]);
                    await publicKeyByUserIdCache.delete(...toDelete);
                    await publicKeyCache.delete(...toDeleteKey);
                    break;
                }
            default:
                break;
        }
    }
});
