<template>
    <MkInfo
        v-if="tlHint && !tlHintClosed"
        :closeable="true"
        class="_gap"
        @close="closeHint"
    >
        <I18n :src="tlHint">
            <template #icon></template>
        </I18n>
    </MkInfo>
    <XNotes
        ref="tlComponent"
        :no-gap="!$store.state.showGapBetweenNotesInTimeline"
        :pagination="pagination"
        @queue="emit('queue', $event)"
    />
</template>

<script lang="ts" setup>
import { onUnmounted, ref } from "vue";
import XNotes from "@/components/MkNotes.vue";
import MkInfo from "@/components/MkInfo.vue";
import * as os from "@/os";
import { stream } from "@/stream";
import * as sound from "@/scripts/sound";
import { $i } from "@/account";
import { i18n } from "@/i18n";
import { defaultStore } from "@/store";
import { endpoints, packed } from "magnetar-common";
import { debounce } from "throttle-debounce";
import * as misskey from "calckey-js";

const props = defineProps<{
    src: string;
    list?: string;
    antenna?: string;
    sound?: boolean;
}>();

const emit = defineEmits<{
    (ev: "note"): void;
    (ev: "queue", count: number): void;
}>();

const displayLimit = 30;

const tlComponent = ref<InstanceType<typeof XNotes>>();

let debounceBuffer: Array<string> = [];
const prependMany = async () => {
    let items = debounceBuffer;
    debounceBuffer = [];

    if (!tlComponent.value?.pagingComponent?.isFresh()) {
        items = items.slice(-displayLimit);
    }

    const notes = (
        await Promise.allSettled(
            items.map((note) =>
                os.magApi(
                    endpoints.GetNoteById,
                    { context: true, attachments: true },
                    {
                        id: note,
                    },
                ),
            ),
        )
    )
        .filter((p) => p.status === "fulfilled")
        .map(
            (p) =>
                (p as PromiseFulfilledResult<packed.PackNoteMaybeFull>).value,
        );

    for (const n of notes) {
        tlComponent.value?.pagingComponent?.prepend(n);

        emit("note");
    }

    if (props.sound) {
        if (notes.some((nn) => nn.user.id !== $i?.id)) sound.play("note");
        if (notes.some((nn) => nn.user.id === $i?.id)) sound.play("noteMy");
    }
};
const debouncePrepend = debounce(40, prependMany);

const prepend = (note: string) => {
    debounceBuffer.push(note);
    debouncePrepend();
};

const onUserAdded = () => {
    tlComponent.value?.pagingComponent?.reload();
};

const onUserRemoved = () => {
    tlComponent.value?.pagingComponent?.reload();
};

const onChangeFollowing = () => {
    if (!tlComponent.value?.pagingComponent?.backed) {
        tlComponent.value?.pagingComponent?.reload();
    }
};

let endpoint: keyof misskey.Endpoints;
let query;
let connection;
let connection2;

let tlHint;
let tlHintClosed;

if (props.src === "antenna") {
    endpoint = "antennas/notes";
    query = {
        antennaId: props.antenna,
    };
    connection = stream.useChannel("antenna", {
        antennaId: props.antenna,
    });
    connection.on("note", prepend);
} else if (props.src === "home") {
    endpoint = "notes/timeline";
    query = {
        withReplies: defaultStore.state.showTimelineReplies,
    };
    connection = stream.useChannel("homeTimeline", {
        withReplies: defaultStore.state.showTimelineReplies,
    });
    connection.on("note", prepend);

    connection2 = stream.useChannel("main");
    connection2.on("follow", onChangeFollowing);
    connection2.on("unfollow", onChangeFollowing);

    tlHint = i18n.ts._tutorial.step5_3;
    tlHintClosed = defaultStore.state.tlHomeHintClosed;
} else if (props.src === "local") {
    endpoint = "notes/local-timeline";
    query = {
        withReplies: defaultStore.state.showTimelineReplies,
    };
    connection = stream.useChannel("localTimeline", {
        withReplies: defaultStore.state.showTimelineReplies,
    });
    connection.on("note", prepend);

    tlHint = i18n.ts._tutorial.step5_4;
    tlHintClosed = defaultStore.state.tlLocalHintClosed;
} else if (props.src === "recommended") {
    endpoint = "notes/recommended-timeline";
    query = {
        withReplies: defaultStore.state.showTimelineReplies,
    };
    connection = stream.useChannel("recommendedTimeline", {
        withReplies: defaultStore.state.showTimelineReplies,
    });
    connection.on("note", prepend);

    tlHint = i18n.ts._tutorial.step5_6;
    tlHintClosed = defaultStore.state.tlRecommendedHintClosed;
} else if (props.src === "social") {
    endpoint = "notes/hybrid-timeline";
    query = {
        withReplies: defaultStore.state.showTimelineReplies,
    };
    connection = stream.useChannel("hybridTimeline", {
        withReplies: defaultStore.state.showTimelineReplies,
    });
    connection.on("note", prepend);

    tlHint = i18n.ts._tutorial.step5_5;
    tlHintClosed = defaultStore.state.tlSocialHintClosed;
} else if (props.src === "global") {
    endpoint = "notes/global-timeline";
    query = {
        withReplies: defaultStore.state.showTimelineReplies,
    };
    connection = stream.useChannel("globalTimeline", {
        withReplies: defaultStore.state.showTimelineReplies,
    });
    connection.on("note", prepend);

    tlHint = i18n.ts._tutorial.step5_7;
    tlHintClosed = defaultStore.state.tlGlobalHintClosed;
} else if (props.src === "mentions") {
    endpoint = "notes/mentions";
    connection = stream.useChannel("main");
    connection.on("mention", prepend);
} else if (props.src === "directs") {
    endpoint = "notes/mentions";
    query = {
        visibility: "specified",
    };
    const onNote = (note) => {
        if (note.visibility === "specified") {
            prepend(note);
        }
    };
    connection = stream.useChannel("main");
    connection.on("mention", onNote);
} else if (props.src === "list") {
    endpoint = "notes/user-list-timeline";
    query = {
        listId: props.list,
    };
    connection = stream.useChannel("userList", {
        listId: props.list,
    });
    connection.on("note", prepend);
    connection.on("userAdded", onUserAdded);
    connection.on("userRemoved", onUserRemoved);
}

function closeHint() {
    switch (props.src) {
        case "home":
            defaultStore.set("tlHomeHintClosed", true);
            break;
        case "local":
            defaultStore.set("tlLocalHintClosed", true);
            break;
        case "recommended":
            defaultStore.set("tlRecommendedHintClosed", true);
            break;
        case "social":
            defaultStore.set("tlSocialHintClosed", true);
            break;
        case "global":
            defaultStore.set("tlGlobalHintClosed", true);
            break;
    }
}

const pagination = {
    endpoint: endpoint,
    limit: 10,
    params: query,
};

onUnmounted(() => {
    connection.dispose();
    if (connection2) connection2.dispose();
});

/* TODO
const timetravel = (date?: Date) => {
	this.date = date;
	this.$refs.tl.reload();
};
*/
</script>
