<script setup>
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
import { debounce, get, isEmpty, size, uniq } from 'lodash'
import { useI18n } from 'vue-i18n'
import { useThemeVars } from 'naive-ui'
import useBrowserStore from 'stores/browser.js'
import { EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js'
import dayjs from 'dayjs'
import Publish from '@/components/icons/Publish.vue'
import Subscribe from '@/components/icons/Subscribe.vue'
import Pause from '@/components/icons/Pause.vue'
import Delete from '@/components/icons/Delete.vue'
import { Publish as PublishSend, StartSubscribe, StopSubscribe } from 'wailsjs/go/services/pubsubService.js'
import Checked from '@/components/icons/Checked.vue'
import Bottom from '@/components/icons/Bottom.vue'
import IconButton from '@/components/common/IconButton.vue'

const themeVars = useThemeVars()

const browserStore = useBrowserStore()
const i18n = useI18n()
const props = defineProps({
    server: {
        type: String,
    },
})

const data = reactive({
    subscribeEvent: '',
    list: [],
    keyword: '',
    autoShowLast: true,
    ellipsisMessage: false,
    channelHistory: [],
})

const publishData = reactive({
    channel: '',
    message: '',
    received: 0,
    lastShowReceived: -1,
})

const tableRef = ref(null)

const columns = computed(() => [
    {
        title: () => i18n.t('pubsub.time'),
        key: 'timestamp',
        width: 180,
        align: 'center',
        titleAlign: 'center',
        render: ({ timestamp }, index) => {
            return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss')
        },
    },
    {
        title: () => i18n.t('pubsub.channel'),
        key: 'channel',
        filterOptionValue: data.client,
        resizable: true,
        filter: (value, row) => {
            return value === '' || row.client === value.toString() || row.addr === value.toString()
        },
        width: 200,
        align: 'center',
        titleAlign: 'center',
        ellipsis: {
            tooltip: {
                style: {
                    maxWidth: '50vw',
                    maxHeight: '50vh',
                },
                scrollable: true,
            },
        },
    },
    {
        title: () => i18n.t('pubsub.message'),
        key: 'message',
        titleAlign: 'center',
        filterOptionValue: data.keyword,
        resizable: true,
        className: 'content-value',
        ellipsis: data.ellipsisMessage
            ? {
                  tooltip: {
                      style: {
                          maxWidth: '50vw',
                          maxHeight: '50vh',
                      },
                      scrollable: true,
                  },
              }
            : undefined,
        filter: (value, row) => {
            return value === '' || !!~row.cmd.indexOf(value.toString())
        },
    },
])

onMounted(() => {
    // try to stop prev subscribe first
    onStopSubscribe()
})

onUnmounted(() => {
    onStopSubscribe()
})

const isSubscribing = computed(() => {
    return !isEmpty(data.subscribeEvent)
})

const publishEnable = computed(() => {
    return !isEmpty(publishData.channel)
})

const _scrollToBottom = () => {
    nextTick(() => {
        tableRef.value?.scrollTo({ position: 'bottom' })
    })
}
const scrollToBottom = debounce(_scrollToBottom, 300, { leading: true, trailing: true })

const onStartSubscribe = async () => {
    if (isSubscribing.value) {
        return
    }

    const { data: ret, success, msg } = await StartSubscribe(props.server)
    if (!success) {
        $message.error(msg)
        return
    }
    data.subscribeEvent = get(ret, 'eventName')
    EventsOn(data.subscribeEvent, (content) => {
        if (content instanceof Array) {
            data.list.push(...content)
        } else {
            data.list.push(content)
        }
        if (data.autoShowLast) {
            scrollToBottom()
        }
    })
}
const onStopSubscribe = async () => {
    const { success, msg } = await StopSubscribe(props.server)
    if (!success) {
        $message.error(msg)
        return
    }

    EventsOff(data.subscribeEvent)
    data.subscribeEvent = ''
}

const onCleanLog = () => {
    data.list = []
}

const onPublish = async () => {
    if (isEmpty(publishData.channel)) {
        return
    }

    const {
        success,
        msg,
        data: { received = 0 },
    } = await PublishSend(props.server, publishData.channel, publishData.message || '')
    if (!success) {
        publishData.received = 0
        if (!isEmpty(msg)) {
            $message.error(msg)
        }
        return
    }
    publishData.message = ''
    publishData.received = received
    publishData.lastShowReceived = Date.now()
    // save channel history
    data.channelHistory = uniq(data.channelHistory.concat(publishData.channel))

    // hide send status after 2 seconds
    setTimeout(() => {
        if (publishData.lastShowReceived > 0 && Date.now() - publishData.lastShowReceived > 2000) {
            publishData.lastShowReceived = -1
        }
    }, 2100)
}
</script>

<template>
    <div class="content-log content-container fill-height flex-box-v">
        <n-form class="flex-item" label-align="left" label-placement="left" label-width="auto" size="small">
            <n-form-item :show-label="false">
                <n-space :wrap="false" :wrap-item="false" style="width: 100%">
                    <n-button
                        v-if="!isSubscribing"
                        :focusable="false"
                        secondary
                        strong
                        type="success"
                        @click="onStartSubscribe">
                        <template #icon>
                            <n-icon :component="Subscribe" size="18" />
                        </template>
                        {{ $t('pubsub.subscribe') }}
                    </n-button>
                    <n-button v-else :focusable="false" secondary strong type="warning" @click="onStopSubscribe">
                        <template #icon>
                            <n-icon :component="Pause" size="18" />
                        </template>
                        {{ $t('pubsub.unsubscribe') }}
                    </n-button>
                    <icon-button
                        :icon="Bottom"
                        :secondary="data.autoShowLast"
                        :type="data.autoShowLast ? 'primary' : 'default'"
                        border
                        size="18"
                        stroke-width="3.5"
                        t-tooltip="monitor.always_show_last"
                        @click="data.autoShowLast = !data.autoShowLast" />
                    <div class="flex-item-expand" />
                    <icon-button
                        :icon="Delete"
                        border
                        size="18"
                        stroke-width="3.5"
                        t-tooltip="pubsub.clear"
                        @click="onCleanLog" />
                </n-space>
            </n-form-item>
        </n-form>
        <n-data-table
            ref="tableRef"
            :columns="columns"
            :data="data.list"
            :loading="data.loading"
            class="flex-item-expand"
            flex-height
            size="small"
            virtual-scroll />
        <div class="total-message">{{ $t('pubsub.receive_message', { total: size(data.list) }) }}</div>
        <div class="flex-box-h publish-input">
            <n-input-group>
                <n-auto-complete
                    v-model:value="publishData.channel"
                    :get-show="() => true"
                    :options="data.channelHistory"
                    :placeholder="$t('pubsub.channel')"
                    style="width: 35%; max-width: 200px"
                    @keydown.enter="onPublish" />
                <n-input
                    v-model:value="publishData.message"
                    :placeholder="$t('pubsub.message')"
                    @keydown.enter="onPublish">
                    <template #suffix>
                        <transition mode="out-in" name="fade">
                            <n-tag v-show="publishData.lastShowReceived > 0" bordered size="small" type="success">
                                <template #icon>
                                    <n-icon :component="Checked" size="16" />
                                </template>
                                {{ publishData.received }}
                            </n-tag>
                        </transition>
                    </template>
                </n-input>
            </n-input-group>
            <n-button :disabled="!publishEnable" type="info" @click="onPublish">
                <template #icon>
                    <n-icon :component="Publish" size="18" />
                </template>
                {{ $t('pubsub.publish') }}
            </n-button>
        </div>
    </div>
</template>

<style lang="scss" scoped>
@use '@/styles/content';

.total-message {
    margin: 10px 0 0;
}

.publish-input {
    margin: 10px 0 0;
    gap: 10px;
}
</style>