import { onUnmounted, Ref, toRaw } from "vue";
import { stream } from "@/stream";
import { $i } from "@/account";
import * as os from "@/os";
import { endpoints, packed } from "magnetar-common";
import { magConvertReaction, magReactionIndex } from "@/scripts-mag/mag-util";

export function useNoteCapture(props: {
    rootEl: Ref<HTMLElement>;
    note: Ref<packed.PackNoteMaybeFull>;
    isDeletedRef: Ref<boolean>;
}) {
    const note = props.note;
    const connection = $i ? stream : null;

    async function onStreamNoteUpdated(noteData): Promise<void> {
        const { type, id, body } = noteData;

        if (id !== note.value.id) return;

        switch (type) {
            case "reacted": {
                const reaction = body.reaction as string;

                const reactionType = magConvertReaction(
                    reaction,
                    body?.emoji?.url,
                );
                const foundReaction = magReactionIndex(
                    note.value.reactions,
                    reactionType,
                );

                const selfReact = ($i && body.userId === $i.id) || false;
                if (foundReaction >= 0) {
                    const [reaction, cnt, selfReactPrev] = toRaw(
                        note.value.reactions[foundReaction],
                    );
                    note.value.reactions[foundReaction] = [
                        reaction,
                        cnt + 1,
                        selfReactPrev || selfReact,
                    ];
                } else {
                    note.value.reactions.push([reactionType, 1, selfReact]);
                }
                break;
            }

            case "unreacted": {
                const reaction = body.reaction;

                const reactionType = magConvertReaction(reaction);
                const foundReaction = magReactionIndex(
                    note.value.reactions,
                    reactionType,
                );

                const selfUnReact = ($i && body.userId === $i.id) || false;
                if (foundReaction >= 0) {
                    const [name, cnt, selfReact] =
                        note.value.reactions[foundReaction];

                    note.value.reactions[foundReaction] = [
                        name,
                        Math.max(0, cnt - 1),
                        !selfUnReact && (selfReact ?? false),
                    ];
                }

                break;
            }

            case "pollVoted": {
                const choice = body.choice;

                if (note.value.poll) {
                    const options = [...note.value.poll.options];
                    options[choice] = {
                        ...options[choice],
                        votes_count: options[choice].votes_count + 1,
                        voted: ($i && body.userId === $i.id) || null,
                    };
                    note.value.poll.options = options;
                }

                break;
            }

            case "deleted": {
                props.isDeletedRef.value = true;
                break;
            }

            case "updated": {
                const editedNote = await os.magApi(
                    endpoints.GetNoteById,
                    {
                        attachments: true,
                        context: true,
                    },
                    { id },
                );

                const keys = new Set<string>();
                Object.keys(editedNote)
                    .concat(Object.keys(note.value))
                    .forEach((key) => keys.add(key));
                keys.forEach((key) => {
                    note.value[key] = editedNote[key];
                });
                break;
            }
        }
    }

    function capture(withHandler = false): void {
        if (connection) {
            // TODO: このノートがストリーミング経由で流れてきた場合のみ sr する
            connection.send(
                document.body.contains(props.rootEl.value) ? "sr" : "s",
                {
                    id: note.value.id,
                },
            );
            if (withHandler) connection.on("noteUpdated", onStreamNoteUpdated);
        }
    }

    function decapture(withHandler = false): void {
        if (connection) {
            connection.send("un", {
                id: note.value.id,
            });
            if (withHandler) connection.off("noteUpdated", onStreamNoteUpdated);
        }
    }

    function onStreamConnected() {
        capture(false);
    }

    capture(true);
    if (connection) {
        connection.on("_connected_", onStreamConnected);
    }

    onUnmounted(() => {
        decapture(true);
        if (connection) {
            connection.off("_connected_", onStreamConnected);
        }
    });
}
