<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import HexMonitor from './HexMonitor.vue'
import KeyPagerInline from './KeyPagerInline.vue'
import KeyNewOrSetDialog from '../../../dialogs/KeyNewOrSetDialog.vue'
import JsonViewDialog from '../../../dialogs/JsonViewDialog.vue'
import { useI18nStore } from '../../../stores/i18n'
import { useRedisStateStore } from '../../../stores/redis-state'
import { useCommonStore } from '../../../stores/common'
import { request } from '../../../stores/socket.service'
import { type Paging, createPaging, rePaging, formatValue, truncateDisplay, isTruncated, copyToClipboard, str } from './key-type-base'

interface StreamEntry { id: string; fields: Array<[string, string]>; data: any; hasDuplicateFields: boolean }

const intlLocaleMap: Record<string, string> = { 'zn': 'zh-CN', 'no': 'nb', 'fil': 'tl' }

function hasDupFields(fields: Array<[string, string]>): boolean {
    const seen = new Set<string>()
    for (const [k] of fields) { if (seen.has(k)) return true; seen.add(k) }
    return false
}

function buildEntries(value: any[]): StreamEntry[] {
    if (!value) return []
    return value.map((entry: any) => {
        const id = entry[0]
        const raw = entry[1]
        const fields: Array<[string, string]> = []
        for (let i = 0; i < raw.length; i += 2) fields.push([raw[i], raw[i + 1]])
        const hasDup = hasDupFields(fields)
        const data = hasDup ? fields.map(([f, v]) => ({ field: f, value: v })) : Object.fromEntries(fields)
        return { id, fields, data, hasDuplicateFields: hasDup }
    })
}

function entryToExport(entry: StreamEntry): any {
    if (entry.hasDuplicateFields) return { id: entry.id, fields: entry.data }
    return { id: entry.id, ...entry.data }
}

const props = defineProps<{ response: any; value: any; valueBuffer: any; keyName: string; valueFormat: string }>()
const emit = defineEmits<{ refresh: [] }>()
const i18n = useI18nStore()
const strings = computed(() => i18n.strings)
const isReadonly = computed(() => useRedisStateStore().connection?.readonly === true)
const common = useCommonStore()

const allEntries = ref<StreamEntry[]>([])
const paging = ref<Paging>(createPaging(0))
const pagedEntries = ref<StreamEntry[]>([])
const editDialogOpen = ref(false)
const editDialogData = ref<any>(null)
const jsonViewOpen = ref(false)
const jsonViewValue = ref('')

function updatePaged(p: Paging) { paging.value = p; pagedEntries.value = allEntries.value.slice(p.startIndex, p.endIndex) }
watch(() => props.value, () => {
    const entries = buildEntries(props.value)
    allEntries.value = entries
    updatePaged(rePaging(paging.value, entries.length))
}, { immediate: true })

function showTimestamp(id: string): string {
    try {
        const ms = parseInt(id.slice(0, id.indexOf('-')))
        const lang = i18n.currentLang || 'en'
        const locale = intlLocaleMap[lang] || lang
        return new Date(ms).toLocaleString(locale, { year: 'numeric', month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' })
    } catch { return id }
}

function addStream() { editDialogData.value = { type: 'append', model: { type: 'stream', key: props.keyName } }; editDialogOpen.value = true }
function handleEditClose(r?: any) { editDialogOpen.value = false; editDialogData.value = null; if (r) emit('refresh') }

async function deleteEntry(id: string) {
    try { await common.confirm({ message: str(strings.value?.confirm?.deleteStreamTimestamp) }); await request({ action: 'key/stream-delete-timestamp', payload: { key: props.keyName, streamTimestamp: id } }); common.toast(str(strings.value?.status?.deletedStreamTimestamp)); emit('refresh') }
    catch (e) { if (e !== undefined) common.generalHandleError(e) }
}

function copyEntry(entry: StreamEntry) { copyToClipboard(JSON.stringify(entryToExport(entry), null, 2)) }
function downloadEntry(entry: StreamEntry) {
    const lines = [entry.id]; for (const [f, v] of entry.fields) { lines.push(f); lines.push(v) }
    const blob = new Blob([lines.join('\n')], { type: 'text/plain' })
    const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${props.keyName}-${entry.id}.txt`; a.click(); URL.revokeObjectURL(url)
}
function viewEntryJson(entry: StreamEntry) { jsonViewValue.value = JSON.stringify(entryToExport(entry)); jsonViewOpen.value = true }
</script>

<template>
    <div>
        <KeyPagerInline :paging="paging" @page-changed="updatePaged" />
        <div class="p3xr-key-table-header">
            <span style="flex:1;">{{ str(strings?.page?.key?.stream?.table?.timestamp) }}</span>
            <span style="text-align:right;"><v-tooltip v-if="!isReadonly" :text="str(strings?.intention?.add)" location="top"><template #activator="{ props: tp }"><v-icon v-bind="tp" style="cursor:pointer;color:inherit;" @click="addStream">mdi-plus</v-icon></template></v-tooltip></span>
        </div>
        <div v-for="(entry, i) in pagedEntries" :key="entry.id" class="p3xr-stream-entry" :class="{ 'p3xr-key-table-odd': i % 2 === 0 }">
            <div class="p3xr-stream-entry-header">
                <div style="display:flex;align-items:center;gap:12px;">
                    <v-tooltip :text="showTimestamp(entry.id)" location="top">
                        <template #activator="{ props: tp }">
                            <strong v-bind="tp" style="cursor:default;">{{ entry.id }}</strong>
                        </template>
                    </v-tooltip>
                    <span style="opacity:0.5;font-size:12px;">{{ showTimestamp(entry.id) }}</span>
                </div>
                <span style="white-space:nowrap;">
                    <v-tooltip v-if="!isReadonly" :text="str(strings?.intention?.delete)" location="top"><template #activator="{ props: tp }"><v-icon v-bind="tp" size="24" class="p3xr-key-icon" style="color:rgb(var(--v-theme-error));" @click="deleteEntry(entry.id)">mdi-delete</v-icon></template></v-tooltip>
                    <v-tooltip :text="str(strings?.intention?.jsonViewShow)" location="top"><template #activator="{ props: tp }"><v-icon v-bind="tp" size="24" class="p3xr-key-icon" style="color:rgb(var(--v-theme-secondary));" @click="viewEntryJson(entry)">mdi-file-tree</v-icon></template></v-tooltip>
                    <v-tooltip :text="str(strings?.intention?.copy)" location="top"><template #activator="{ props: tp }"><v-icon v-bind="tp" size="24" class="p3xr-key-icon" style="color:rgb(var(--v-theme-secondary));" @click="copyEntry(entry)">mdi-content-copy</v-icon></template></v-tooltip>
                    <v-tooltip :text="str(strings?.intention?.downloadBuffer)" location="top"><template #activator="{ props: tp }"><v-icon v-bind="tp" size="24" class="p3xr-key-icon" style="color:rgb(var(--v-theme-secondary));" @click="downloadEntry(entry)">mdi-download</v-icon></template></v-tooltip>
                </span>
            </div>
            <div class="p3xr-stream-entry-fields">
                <div v-for="([field, val], fi) in entry.fields" :key="`${field}-${fi}`" class="p3xr-stream-field-row">
                    <span style="min-width:120px;opacity:0.7;">{{ field }}</span>
                    <span class="p3xr-stream-field-value">
                        <HexMonitor v-if="valueFormat === 'hex'" :value="truncateDisplay(val)" />
                        <template v-else>{{ truncateDisplay(formatValue(val, valueFormat)) }}<span v-if="isTruncated(val)" style="opacity:0.5;">...</span></template>
                    </span>
                </div>
            </div>
        </div>
        <KeyNewOrSetDialog :open="editDialogOpen" :data="editDialogData" @close="handleEditClose" />
        <JsonViewDialog :open="jsonViewOpen" :value="jsonViewValue" @close="jsonViewOpen = false" />
    </div>
</template>

<style src="./key-table.css"></style>
<style scoped>
.p3xr-stream-entry { border-bottom: 1px solid rgba(var(--v-theme-on-surface), 0.05); }
.p3xr-stream-entry:hover { background-color: rgba(var(--v-theme-on-surface), 0.1) !important; }
.p3xr-stream-entry-header { display: flex; align-items: center; justify-content: space-between; padding: 6px 16px; font-size: 13px; }
.p3xr-stream-entry-fields { padding: 0 16px 8px; overflow: auto; max-height: 300px; }
.p3xr-stream-field-row { display: flex; gap: 8px; padding: 2px 0; }
.p3xr-stream-field-value { flex: 1; max-height: 200px; overflow: auto; white-space: pre-wrap; word-break: break-all; font-family: 'Roboto Mono', monospace; }
</style>
