<script setup lang="ts">
import type { DataTableColumns } from 'naive-ui'
import { computed, h, ref, watch } from 'vue'
import {
    NButton,
    NCard,
    NDataTable,
    NDivider,
    NInput,
    NList,
    NListItem,
    NModal,
    NPopconfirm,
    NSpace,
    NTabPane,
    NTabs,
    NThing,
    useMessage
} from 'naive-ui'
import PromptRecommend from '../../assets/recommend.json'
import { SvgIcon } from '.'
import { usePromptStore } from '@/store'
import { useBasicLayout } from '@/hooks/useBasicLayout'
import { t } from '@/locales'

interface DataProps {
    renderKey: string
    renderValue: string
    key: string
    value: string
}

interface Props {
    visible: boolean
}

interface Emit {
    (e: 'update:visible', visible: boolean): void
}

const props = defineProps<Props>()

const emit = defineEmits<Emit>()

const message = useMessage()

const show = computed({
    get: () => props.visible,
    set: (visible: boolean) => emit('update:visible', visible)
})

const showModal = ref(false)

const importLoading = ref(false)
const exportLoading = ref(false)

const searchValue = ref<string>('')

// 移动端自适应相关
const { isMobile } = useBasicLayout()

const promptStore = usePromptStore()

// Prompt在线导入推荐List,根据部署者喜好进行修改(assets/recommend.json)
const promptRecommendList = PromptRecommend
const promptList = ref<any>(promptStore.promptList)

// 用于添加修改的临时prompt参数
const tempPromptKey = ref('')
const tempPromptValue = ref('')

// Modal模式，根据不同模式渲染不同的Modal内容
const modalMode = ref('')

// 这个是为了后期的修改Prompt内容考虑，因为要针对无uuid的list进行修改，且考虑到不能出现标题和内容的冲突，所以就需要一个临时item来记录一下
const tempModifiedItem = ref<any>({})

// 添加修改导入都使用一个Modal, 临时修改内容占用tempPromptKey,切换状态前先将内容都清楚
const changeShowModal = (mode: 'add' | 'modify' | 'local_import', selected = { key: '', value: '' }) => {
    if (mode === 'add') {
        tempPromptKey.value = ''
        tempPromptValue.value = ''
    } else if (mode === 'modify') {
        tempModifiedItem.value = { ...selected }
        tempPromptKey.value = selected.key
        tempPromptValue.value = selected.value
    } else if (mode === 'local_import') {
        tempPromptKey.value = 'local_import'
        tempPromptValue.value = ''
    }
    showModal.value = !showModal.value
    modalMode.value = mode
}

// 在线导入相关
const downloadURL = ref('')
const downloadDisabled = computed(() => downloadURL.value.trim().length < 1)
const setDownloadURL = (url: string) => {
    downloadURL.value = url
}

// 控制 input 按钮
const inputStatus = computed(() => tempPromptKey.value.trim().length < 1 || tempPromptValue.value.trim().length < 1)

// Prompt模板相关操作
const addPromptTemplate = () => {
    for (const i of promptList.value) {
        if (i.key === tempPromptKey.value) {
            message.error(t('store.addRepeatTitleTips'))
            return
        }
        if (i.value === tempPromptValue.value) {
            message.error(t('store.addRepeatContentTips', { msg: tempPromptKey.value }))
            return
        }
    }
    promptList.value.unshift({ key: tempPromptKey.value, value: tempPromptValue.value } as never)
    message.success(t('common.addSuccess'))
    changeShowModal('add')
}

const modifyPromptTemplate = () => {
    let index = 0

    // 通过临时索引把待修改项摘出来
    for (const i of promptList.value) {
        if (i.key === tempModifiedItem.value.key && i.value === tempModifiedItem.value.value) break
        index = index + 1
    }

    const tempList = promptList.value.filter((_: any, i: number) => i !== index)

    // 搜索有冲突的部分
    for (const i of tempList) {
        if (i.key === tempPromptKey.value) {
            message.error(t('store.editRepeatTitleTips'))
            return
        }
        if (i.value === tempPromptValue.value) {
            message.error(t('store.editRepeatContentTips', { msg: i.key }))
            return
        }
    }

    promptList.value = [{ key: tempPromptKey.value, value: tempPromptValue.value }, ...tempList] as never
    message.success(t('common.editSuccess'))
    changeShowModal('modify')
}

const deletePromptTemplate = (row: { key: string; value: string }) => {
    promptList.value = [
        ...promptList.value.filter((item: { key: string; value: string }) => item.key !== row.key)
    ] as never
    message.success(t('common.deleteSuccess'))
}

const clearPromptTemplate = () => {
    promptList.value = []
    message.success(t('common.clearSuccess'))
}

const importPromptTemplate = (from = 'online') => {
    try {
        const jsonData = JSON.parse(tempPromptValue.value)
        let key = ''
        let value = ''
        // 可以扩展加入更多模板字典的key
        if ('key' in jsonData[0]) {
            key = 'key'
            value = 'value'
        } else if ('act' in jsonData[0]) {
            key = 'act'
            value = 'prompt'
        } else {
            // 不支持的字典的key防止导入 以免破坏prompt商店打开
            message.warning('prompt key not supported.')
            throw new Error('prompt key not supported.')
        }

        for (const i of jsonData) {
            if (!(key in i) || !(value in i)) throw new Error(t('store.importError'))
            let safe = true
            for (const j of promptList.value) {
                if (j.key === i[key]) {
                    message.warning(t('store.importRepeatTitle', { msg: i[key] }))
                    safe = false
                    break
                }
                if (j.value === i[value]) {
                    message.warning(t('store.importRepeatContent', { msg: i[key] }))
                    safe = false
                    break
                }
            }
            if (safe) promptList.value.unshift({ key: i[key], value: i[value] } as never)
        }
        message.success(t('common.importSuccess'))
    } catch {
        message.error('JSON 格式错误，请检查 JSON 格式')
    }
    if (from === 'local') showModal.value = !showModal.value
}

// 模板导出
const exportPromptTemplate = () => {
    exportLoading.value = true
    const jsonDataStr = JSON.stringify(promptList.value)
    const blob = new Blob([jsonDataStr], { type: 'application/json' })
    const url = URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = url
    link.download = 'ChatGPTPromptTemplate.json'
    link.click()
    URL.revokeObjectURL(url)
    exportLoading.value = false
}

// 模板在线导入
const downloadPromptTemplate = async () => {
    try {
        importLoading.value = true
        const response = await fetch(downloadURL.value)
        const jsonData = await response.json()
        if ('key' in jsonData[0] && 'value' in jsonData[0]) tempPromptValue.value = JSON.stringify(jsonData)
        if ('act' in jsonData[0] && 'prompt' in jsonData[0]) {
            const newJsonData = jsonData.map((item: { act: string; prompt: string }) => {
                return {
                    key: item.act,
                    value: item.prompt
                }
            })
            tempPromptValue.value = JSON.stringify(newJsonData)
        }
        importPromptTemplate()
        downloadURL.value = ''
    } catch {
        message.error(t('store.downloadError'))
        downloadURL.value = ''
    } finally {
        importLoading.value = false
    }
}

// 移动端自适应相关
const renderTemplate = () => {
    const [keyLimit, valueLimit] = isMobile.value ? [10, 30] : [15, 50]

    return promptList.value.map((item: { key: string; value: string }) => {
        return {
            renderKey: item.key.length <= keyLimit ? item.key : `${item.key.substring(0, keyLimit)}...`,
            renderValue: item.value.length <= valueLimit ? item.value : `${item.value.substring(0, valueLimit)}...`,
            key: item.key,
            value: item.value
        }
    })
}

const pagination = computed(() => {
    const [pageSize, pageSlot] = isMobile.value ? [6, 5] : [7, 15]
    return {
        pageSize,
        pageSlot
    }
})

// table相关
const createColumns = (): DataTableColumns<DataProps> => {
    return [
        {
            title: t('store.title'),
            key: 'renderKey'
        },
        {
            title: t('store.description'),
            key: 'renderValue'
        },
        {
            title: t('common.action'),
            key: 'actions',
            width: 100,
            align: 'center',
            render(row) {
                return h(
                    'div',
                    { class: 'flex items-center flex-col gap-2' },
                    {
                        default: () => [
                            h(
                                NButton,
                                {
                                    tertiary: true,
                                    size: 'small',
                                    type: 'info',
                                    onClick: () => changeShowModal('modify', row)
                                },
                                { default: () => t('common.edit') }
                            ),
                            h(
                                NButton,
                                {
                                    tertiary: true,
                                    size: 'small',
                                    type: 'error',
                                    onClick: () => deletePromptTemplate(row)
                                },
                                { default: () => t('common.delete') }
                            )
                        ]
                    }
                )
            }
        }
    ]
}

const columns = createColumns()

watch(
    () => promptList,
    () => {
        promptStore.updatePromptList(promptList.value)
    },
    { deep: true }
)

const dataSource = computed(() => {
    const data = renderTemplate()
    const value = searchValue.value
    if (value && value !== '') {
        return data.filter((item: DataProps) => {
            return item.renderKey.includes(value) || item.renderValue.includes(value)
        })
    }
    return data
})
</script>

<template>
    <NModal v-model:show="show" style="width: 90%; max-width: 900px" preset="card">
        <div class="space-y-4">
            <NTabs type="segment">
                <NTabPane name="local" :tab="$t('store.local')">
                    <div class="flex gap-3 mb-4" :class="[isMobile ? 'flex-col' : 'flex-row justify-between']">
                        <div class="flex items-center space-x-4">
                            <NButton type="primary" size="small" @click="changeShowModal('add')">
                                {{ $t('common.add') }}
                            </NButton>
                            <NButton size="small" @click="changeShowModal('local_import')">
                                {{ $t('common.import') }}
                            </NButton>
                            <NButton size="small" :loading="exportLoading" @click="exportPromptTemplate()">
                                {{ $t('common.export') }}
                            </NButton>
                            <NPopconfirm @positive-click="clearPromptTemplate">
                                <template #trigger>
                                    <NButton size="small">
                                        {{ $t('common.clear') }}
                                    </NButton>
                                </template>
                                {{ $t('store.clearStoreConfirm') }}
                            </NPopconfirm>
                        </div>
                        <div class="flex items-center">
                            <NInput v-model:value="searchValue" style="width: 100%" />
                        </div>
                    </div>
                    <NDataTable
                        v-if="!isMobile"
                        :max-height="400"
                        :columns="columns"
                        :data="dataSource"
                        :pagination="pagination"
                        :bordered="false"
                    />
                    <NList v-if="isMobile" style="max-height: 400px; overflow-y: auto">
                        <NListItem v-for="(item, index) of dataSource" :key="index">
                            <NThing :title="item.renderKey" :description="item.renderValue" />
                            <template #suffix>
                                <div class="flex flex-col items-center gap-2">
                                    <NButton tertiary size="small" type="info" @click="changeShowModal('modify', item)">
                                        {{ t('common.edit') }}
                                    </NButton>
                                    <NButton tertiary size="small" type="error" @click="deletePromptTemplate(item)">
                                        {{ t('common.delete') }}
                                    </NButton>
                                </div>
                            </template>
                        </NListItem>
                    </NList>
                </NTabPane>
                <NTabPane name="download" :tab="$t('store.online')">
                    <p class="mb-4">
                        {{ $t('store.onlineImportWarning') }}
                    </p>
                    <div class="flex items-center gap-4">
                        <NInput v-model:value="downloadURL" placeholder="" />
                        <NButton
                            strong
                            secondary
                            :disabled="downloadDisabled"
                            :loading="importLoading"
                            @click="downloadPromptTemplate()"
                        >
                            {{ $t('common.download') }}
                        </NButton>
                    </div>
                    <NDivider />
                    <div class="max-h-[360px] overflow-y-auto space-y-4">
                        <NCard
                            v-for="info in promptRecommendList"
                            :key="info.key"
                            :title="info.key"
                            :bordered="true"
                            embedded
                        >
                            <p class="overflow-hidden text-ellipsis whitespace-nowrap" :title="info.desc">
                                {{ info.desc }}
                            </p>
                            <template #footer>
                                <div class="flex items-center justify-end space-x-4">
                                    <NButton text>
                                        <a :href="info.url" target="_blank">
                                            <SvgIcon class="text-xl" icon="ri:link" />
                                        </a>
                                    </NButton>
                                    <NButton text @click="setDownloadURL(info.downloadUrl)">
                                        <SvgIcon class="text-xl" icon="ri:add-fill" />
                                    </NButton>
                                </div>
                            </template>
                        </NCard>
                    </div>
                </NTabPane>
            </NTabs>
        </div>
    </NModal>

    <NModal v-model:show="showModal" style="width: 90%; max-width: 600px" preset="card">
        <NSpace v-if="modalMode === 'add' || modalMode === 'modify'" vertical>
            {{ t('store.title') }}
            <NInput v-model:value="tempPromptKey" />
            {{ t('store.description') }}
            <NInput v-model:value="tempPromptValue" type="textarea" />
            <NButton
                block
                type="primary"
                :disabled="inputStatus"
                @click="
                    () => {
                        modalMode === 'add' ? addPromptTemplate() : modifyPromptTemplate()
                    }
                "
            >
                {{ t('common.confirm') }}
            </NButton>
        </NSpace>
        <NSpace v-if="modalMode === 'local_import'" vertical>
            <NInput
                v-model:value="tempPromptValue"
                :placeholder="t('store.importPlaceholder')"
                :autosize="{ minRows: 3, maxRows: 15 }"
                type="textarea"
            />
            <NButton
                block
                type="primary"
                :disabled="inputStatus"
                @click="
                    () => {
                        importPromptTemplate('local')
                    }
                "
            >
                {{ t('common.import') }}
            </NButton>
        </NSpace>
    </NModal>
</template>
