<script setup lang="ts">
import { MdPreview } from 'md-editor-v3'
import 'md-editor-v3/lib/style.css'
import { marked } from 'marked'
import api from '@/api'
import type { TableColumnCtx } from 'element-plus'
import { format } from 'date-fns'
import { getUserName } from '@/composables/useAuth'

interface TableRow {
    id: number
    tags: string[]
    content: string
    created_at: string
    updated_at: string
}

interface ListItem {
    value: string
    label: string
}

interface NoteDialogConfig {
    visible: boolean
    content: string
    created_at: string
    id: number
    tags: string[]
    tagOptions: ListItem[]
    loading: boolean
    mdPreviewShow: boolean
}

const allTagList = ref<ListItem[]>([])
const tableTagList = ref<ListItem[]>([])
const tableData = ref<TableRow[]>([])
const noteSearch = ref('')
const tagSearch = ref<string[]>([])
const tagOptions = ref<ListItem[]>()
const filterLoading = ref(false)
const totalNotes = ref(0)
const currentPage = ref(1)
const pageSize = ref(20)
const mdTextAreaRef = ref<HTMLTextAreaElement | null>()
const noteDialogConfig = reactive<NoteDialogConfig>({
    visible: false,
    content: '',
    created_at: '',
    id: -1,
    tags: [],
    tagOptions: [],
    loading: false,
    mdPreviewShow: true,
})

function getTableDataByPage(
    pageNum: number,
    pageSize: number,
    noteFilter: string = '',
    tagFilter: Array<string> = [],
): void {
    api
        .post('/note/getByPage', {
            page: pageNum,
            pageSize: pageSize,
            noteFilter: noteFilter,
            tagFilter: tagFilter,
            name: getUserName(),
        })
        .then((res) => {
            tableData.value = res.data
        })
        .catch((error) => {
            console.error('getByPage error', error)
        })
}

function getTotalSize(noteFilter: string = '', tagFilter: Array<string> = []) {
    api
        .post('/note/getTotal', {
            name: getUserName(),
            noteFilter: noteFilter,
            tagFilter: tagFilter,
        })
        .then((res) => {
            totalNotes.value = res.data
        })
}

function getTagList(noteFilter: string = '', tagFilter: Array<string> = []) {
    api
        .post('/note/getAllTags', {
            name: getUserName(),
            noteFilter: noteFilter,
            tagFilter: tagFilter,
        })
        .then((res) => {
            tableTagList.value = res.data.map((tag: string) => ({
                value: tag.toLowerCase(),
                label: tag.toLowerCase(),
            }))
            if (!noteFilter && !tagFilter.length && !allTagList.value.length) {
                allTagList.value = tableTagList.value
            }
        })
}

onMounted(() => {
    getTableDataByPage(currentPage.value, pageSize.value)
    getTotalSize()
    getTagList()
})

const handleClose = (done: () => void) => {
    ElMessageBox.confirm('Close without saving?')
        .then(() => {
            done()
        })
        .catch(() => {})
}

const handleNoteSearchChange = (note: string) => {
    getTableDataByPage(currentPage.value, pageSize.value, note, tagSearch.value)
    getTotalSize(note, tagSearch.value)
    getTagList(note, tagSearch.value)
}

const toggleTag = (tag: string) => {
    const index = tagSearch.value.indexOf(tag)
    if (index === -1) {
        tagSearch.value = [...tagSearch.value, tag]
    } else {
        tagSearch.value = tagSearch.value.filter(t => t !== tag)
    }
    handleTagSearchChange(tagSearch.value)
}

const clearTags = () => {
    tagSearch.value = []
    handleTagSearchChange([])
}

const newTagInput = ref('')

const addTag = () => {
    const tag = newTagInput.value.trim().toLowerCase()
    if (tag && !noteDialogConfig.tags.includes(tag)) {
        noteDialogConfig.tags.push(tag)
    }
    newTagInput.value = ''
}

const removeTag = (index: number) => {
    noteDialogConfig.tags.splice(index, 1)
}

const quickAddTag = (tag: string) => {
    if (!noteDialogConfig.tags.includes(tag)) {
        noteDialogConfig.tags.push(tag)
    }
}

const handleTagFilterSelect = (query: string) => {
    if (query) {
        filterLoading.value = true
        tagOptions.value = tableTagList.value.filter((item) => {
            return item.label.toLowerCase().includes(query.toLowerCase())
        })
        filterLoading.value = false
    } else {
        tagOptions.value = tableTagList.value
    }
}

const handleNewNoteTagSearch = (query: string) => {
    if (query) {
        noteDialogConfig.loading = true
        noteDialogConfig.tagOptions = allTagList.value.filter((item) => {
            return item.label.toLowerCase().includes(query.toLowerCase())
        })
        noteDialogConfig.loading = false
    } else {
        noteDialogConfig.tagOptions = allTagList.value
    }
}

const handleTagSearchChange = (value: string[]) => {
    getTableDataByPage(currentPage.value, pageSize.value, noteSearch.value, value)
    getTotalSize(noteSearch.value, value)
    getTagList(noteSearch.value, value)
}

const handlePageChange = (val: number) => {
    getTableDataByPage(val, pageSize.value, noteSearch.value, tagSearch.value)
}

const handleRowClick = (row: TableRow, column: TableColumnCtx<TableRow>, event: Event): void => {
    noteDialogConfig.visible = true
    noteDialogConfig.content = row.content
    noteDialogConfig.tags = row.tags
    noteDialogConfig.created_at = row.created_at
    noteDialogConfig.id = row.id
}

const handlemdTextEditBlur = () => {
    if (noteDialogConfig.content === '') return
    noteDialogConfig.mdPreviewShow = true
}

const handleMdClick = (event: MouseEvent) => {
    if (noteDialogConfig.loading) return
    noteDialogConfig.mdPreviewShow = false
    nextTick(() => {
        mdTextAreaRef.value?.focus()
    })
}

const handleNewNoteClick = () => {
    noteDialogConfig.id = -1
    noteDialogConfig.content = ''
    noteDialogConfig.created_at = format(new Date(), 'yyyy-MM-dd HH:mm:ss')
    noteDialogConfig.tags = []
    noteDialogConfig.visible = true
    noteDialogConfig.mdPreviewShow = false
}

const handleNoteChange = () => {
    noteDialogConfig.loading = true
    if (noteDialogConfig.content.length === 0) {
        noteDialogConfig.visible = false
        noteDialogConfig.loading = false
        return
    }
    if (noteDialogConfig.id === -1) {
        api
            .post('/note/newNote', {
                content: noteDialogConfig.content,
                tags: noteDialogConfig.tags,
                created_at: noteDialogConfig.created_at,
                name: getUserName(),
            })
            .then((response) => {
                tableData.value.unshift({
                    id: response.data,
                    content: noteDialogConfig.content,
                    created_at: format(new Date(noteDialogConfig.created_at), 'yyyy-MM-dd HH:mm:ss'),
                    updated_at: format(new Date(noteDialogConfig.created_at), 'yyyy-MM-dd HH:mm:ss'),
                    tags: noteDialogConfig.tags,
                })
                noteDialogConfig.visible = false
                noteDialogConfig.loading = false
            })
            .catch((error) => {
                console.log(error)
                noteDialogConfig.visible = false
                noteDialogConfig.loading = false
            })
            .finally(() => {
                noteDialogConfig.visible = false
                noteDialogConfig.loading = false
            })
    } else {
        api
            .post('/note/updateNote', {
                id: noteDialogConfig.id,
                content: noteDialogConfig.content,
                tags: noteDialogConfig.tags,
                created_at: noteDialogConfig.created_at,
                name: getUserName(),
            })
            .then((response) => {
                const index = tableData.value.findIndex((row) => row.id === noteDialogConfig.id)
                if (index !== -1) {
                    Object.assign(tableData.value[index]!, {
                        content: noteDialogConfig.content,
                        created_at: format(new Date(noteDialogConfig.created_at), 'yyyy-MM-dd HH:mm:ss'),
                        tags: noteDialogConfig.tags,
                    })
                }
                noteDialogConfig.visible = false
                noteDialogConfig.loading = false
            })
            .catch((error) => {
                console.log(error)
                noteDialogConfig.visible = false
                noteDialogConfig.loading = false
            })
            .finally(() => {
                noteDialogConfig.visible = false
                noteDialogConfig.loading = false
            })
    }
}

const hanldeNoteDelete = () => {
    noteDialogConfig.loading = true
    let displayContent = marked(noteDialogConfig.content)
        .toString()
        .replace(/(<([^>]+)>)/gi, '')
    displayContent =
        displayContent.length > 30 ? displayContent.substring(0, 30) + '...' : displayContent
    ElMessageBox.confirm('Note【' + displayContent + '】will be deleted. Continue?', 'Warning', {
        confirmButtonText: 'OK',
        cancelButtonText: 'Cancel',
        type: 'warning',
    })
        .then(() => {
            api
                .post('/note/deleteNote', {
                    id: noteDialogConfig.id,
                    name: getUserName(),
                })
                .then((res) => {
                    if (res.data) {
                        const index = tableData.value.findIndex((row) => row.id === noteDialogConfig.id)
                        if (index !== -1) {
                            tableData.value.splice(index, 1)
                        }
                    }
                })
            noteDialogConfig.loading = false
            noteDialogConfig.visible = false
            ElMessage({
                type: 'success',
                message: 'Delete completed',
            })
        })
        .catch(() => {
            noteDialogConfig.loading = false
        })
}

const getPreviewText = (content: string) => {
    return marked(content).toString().replace(/(<([^>]+)>)/gi, '')
}
</script>

<template>
    <div class="note-container">
        <div class="note-header">
            <span class="header-icon">//</span>
            <h2>Notes</h2>
            <span class="note-count">{{ totalNotes }} entries</span>
            <button class="new-btn" @click="handleNewNoteClick">+ New</button>
        </div>

        <div class="filter-bar">
            <input
                v-model="noteSearch"
                type="text"
                class="search-input"
                placeholder="搜索内容..."
                @change="handleNoteSearchChange(noteSearch)"
            />
        </div>

        <div class="tag-filter" v-if="allTagList.length > 0">
            <span class="filter-label">标签筛选:</span>
            <div class="tag-cloud">
                <button
                    v-for="tag in allTagList"
                    :key="tag.value"
                    :class="['tag-btn', { active: tagSearch.includes(tag.value) }]"
                    @click="toggleTag(tag.value)"
                >
                    {{ tag.label }}
                </button>
            </div>
            <button v-if="tagSearch.length > 0" class="clear-btn" @click="clearTags">
                清除筛选
            </button>
        </div>

        <div class="note-list">
            <div
                v-for="note in tableData"
                :key="note.id"
                class="note-item"
                @click="handleRowClick(note, {} as any, $event)"
            >
                <div class="note-content">
                    {{ getPreviewText(note.content) }}
                </div>
                <div class="note-meta">
                    <div class="note-tags">
                        <span v-for="tag in note.tags" :key="tag" class="tag">{{ tag }}</span>
                    </div>
                    <span class="note-date">{{ note.created_at.split('T')[0] }}</span>
                </div>
            </div>
        </div>

        <div class="pagination" v-if="totalNotes > pageSize">
            <button class="page-btn" :disabled="currentPage === 1" @click="handlePageChange(currentPage - 1)">← Prev</button>
            <span class="page-info">{{ currentPage }} / {{ Math.ceil(totalNotes / pageSize) }}</span>
            <button class="page-btn" :disabled="currentPage * pageSize >= totalNotes" @click="handlePageChange(currentPage + 1)">Next →</button>
        </div>
    </div>

    <el-dialog
        v-model="noteDialogConfig.visible"
        :title="noteDialogConfig.id === -1 ? 'New Note' : 'Edit Note'"
        :before-close="handleClose"
        class="note-dialog"
        width="500px"
    >
        <div class="dialog-content">
            <div class="form-group">
                <label class="form-label">Content</label>
                <textarea
                    v-show="!noteDialogConfig.mdPreviewShow"
                    ref="mdTextAreaRef"
                    v-model="noteDialogConfig.content"
                    class="form-textarea"
                    placeholder="Write your note..."
                    @blur="handlemdTextEditBlur"
                    :disabled="noteDialogConfig.loading"
                ></textarea>
                <div
                    v-show="noteDialogConfig.mdPreviewShow"
                    class="md-preview"
                    @click="handleMdClick"
                >
                    <MdPreview v-model="noteDialogConfig.content" min-height="1em" />
                </div>
            </div>

            <div class="form-group">
                <label class="form-label">Tags</label>
                <div class="tag-input-wrapper">
                    <input
                        v-model="newTagInput"
                        type="text"
                        class="tag-input"
                        placeholder="输入标签后按回车添加"
                        @keydown.enter.prevent="addTag"
                    />
                    <button class="add-tag-btn" @click="addTag">+</button>
                </div>
                <div class="selected-tags" v-if="noteDialogConfig.tags.length > 0">
                    <span
                        v-for="(tag, index) in noteDialogConfig.tags"
                        :key="index"
                        class="selected-tag"
                    >
                        {{ tag }}
                        <button class="remove-tag" @click="removeTag(index)">×</button>
                    </span>
                </div>
                <div class="existing-tags" v-if="allTagList.length > 0">
                    <span class="hint">已有标签:</span>
                    <button
                        v-for="tag in allTagList.filter(t => !noteDialogConfig.tags.includes(t.value))"
                        :key="tag.value"
                        class="quick-tag-btn"
                        @click="quickAddTag(tag.value)"
                    >
                        {{ tag.label }}
                    </button>
                </div>
            </div>

            <div class="form-group">
                <label class="form-label">Date</label>
                <input
                    v-model="noteDialogConfig.created_at"
                    type="datetime-local"
                    class="form-input"
                    :disabled="noteDialogConfig.loading"
                />
            </div>
        </div>

        <template #footer>
            <div class="dialog-footer">
                <button class="btn btn-secondary" @click="noteDialogConfig.visible = false">Cancel</button>
                <button
                    v-if="noteDialogConfig.id > 0"
                    class="btn btn-danger"
                    @click="hanldeNoteDelete"
                >Delete</button>
                <button class="btn btn-primary" @click="handleNoteChange">Confirm</button>
            </div>
        </template>
    </el-dialog>
</template>

<style scoped>
.note-container {
    max-width: 900px;
    margin: 0 auto;
    padding: 2rem 1.5rem;
}

.note-header {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
    padding-bottom: 1rem;
    border-bottom: 2px solid #e1e4e8;
}

.header-icon {
    color: #2563eb;
    font-family: var(--font-mono);
    font-size: 1.2rem;
}

.note-header h2 {
    font-size: 1.5rem;
    font-weight: 600;
    color: #1f2937;
    margin: 0;
    font-family: var(--font-mono);
}

.note-count {
    font-size: 0.8rem;
    color: #6b7280;
    font-family: var(--font-mono);
}

.new-btn {
    margin-left: auto;
    padding: 0.5rem 1rem;
    background: #2563eb;
    color: white;
    border: none;
    border-radius: 6px;
    font-size: 0.85rem;
    font-family: var(--font-mono);
    cursor: pointer;
    transition: all 0.2s ease;
}

.new-btn:hover {
    background: #1d4ed8;
}

.filter-bar {
    margin-bottom: 1rem;
}

.search-input {
    width: 100%;
    padding: 0.6rem 0.8rem;
    border: 1px solid #e5e7eb;
    border-radius: 6px;
    font-size: 0.9rem;
    font-family: var(--font-mono);
    background: #fafafa;
    color: #1f2937;
    transition: all 0.2s ease;
}

.search-input:focus {
    outline: none;
    border-color: #2563eb;
    background: #ffffff;
}

.tag-filter {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    margin-bottom: 1.5rem;
    flex-wrap: wrap;
}

.filter-label {
    font-size: 0.8rem;
    font-family: var(--font-mono);
    color: #6b7280;
    padding-top: 0.4rem;
}

.tag-cloud {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    flex: 1;
}

.tag-btn {
    padding: 0.3rem 0.75rem;
    background: #f3f4f6;
    border: 1px solid #e5e7eb;
    border-radius: 4px;
    font-size: 0.8rem;
    font-family: var(--font-mono);
    color: #6b7280;
    cursor: pointer;
}

.tag-btn:hover:not(.active) {
    background: #e5e7eb;
}

.tag-btn.active {
    background: #2563eb;
    border-color: #2563eb;
    color: #ffffff;
}

.clear-btn {
    padding: 0.3rem 0.75rem;
    background: transparent;
    border: none;
    font-size: 0.8rem;
    font-family: var(--font-mono);
    color: #9ca3af;
    cursor: pointer;
}

.clear-btn:hover {
    color: #dc2626;
}

.note-list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.note-item {
    padding: 1rem 1.25rem;
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.2s ease;
}

.note-item:hover {
    border-color: #2563eb;
    box-shadow: 0 2px 8px rgba(37, 99, 235, 0.1);
}

.note-content {
    color: #1f2937;
    font-size: 0.95rem;
    line-height: 1.5;
    margin-bottom: 0.75rem;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.note-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.note-tags {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.tag {
    font-size: 0.7rem;
    font-family: var(--font-mono);
    padding: 0.2rem 0.5rem;
    background: #eff6ff;
    color: #2563eb;
    border-radius: 4px;
}

.note-date {
    font-size: 0.8rem;
    font-family: var(--font-mono);
    color: #9ca3af;
}

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid #e5e7eb;
}

.page-btn {
    padding: 0.5rem 1rem;
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 6px;
    font-size: 0.85rem;
    font-family: var(--font-mono);
    color: #4b5563;
    cursor: pointer;
    transition: all 0.2s ease;
}

.page-btn:hover:not(:disabled) {
    border-color: #2563eb;
    color: #2563eb;
}

.page-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.page-info {
    font-size: 0.85rem;
    font-family: var(--font-mono);
    color: #6b7280;
}

/* Dialog styles */
.dialog-content {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.form-group {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.form-label {
    font-size: 0.8rem;
    font-family: var(--font-mono);
    color: #6b7280;
    font-weight: 500;
}

.form-input,
.form-textarea,
.form-select {
    padding: 0.6rem 0.8rem;
    border: 1px solid #e5e7eb;
    border-radius: 6px;
    font-size: 0.9rem;
    font-family: var(--font-mono);
    background: #fafafa;
    color: #1f2937;
    transition: all 0.2s ease;
}

.form-textarea {
    min-height: 120px;
    resize: vertical;
}

.form-input:focus,
.form-textarea:focus,
.form-select:focus {
    outline: none;
    border-color: #2563eb;
    background: #ffffff;
}

.tag-input-wrapper {
    display: flex;
    gap: 0.5rem;
}

.tag-input {
    flex: 1;
    padding: 0.5rem 0.75rem;
    border: 1px solid #e5e7eb;
    border-radius: 6px;
    font-size: 0.9rem;
    font-family: var(--font-mono);
    background: #fafafa;
    color: #1f2937;
}

.tag-input:focus {
    outline: none;
    border-color: #2563eb;
    background: #ffffff;
}

.add-tag-btn {
    padding: 0.5rem 0.75rem;
    background: #2563eb;
    color: white;
    border: none;
    border-radius: 6px;
    font-size: 1rem;
    cursor: pointer;
}

.add-tag-btn:hover {
    background: #1d4ed8;
}

.selected-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.75rem;
}

.selected-tag {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.25rem 0.5rem;
    background: #2563eb;
    color: white;
    border-radius: 4px;
    font-size: 0.8rem;
    font-family: var(--font-mono);
}

.remove-tag {
    background: none;
    border: none;
    color: white;
    opacity: 0.7;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0;
}

.remove-tag:hover {
    opacity: 1;
}

.existing-tags {
    margin-top: 0.75rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.existing-tags .hint {
    font-size: 0.75rem;
    color: #9ca3af;
    font-family: var(--font-mono);
}

.quick-tag-btn {
    padding: 0.2rem 0.5rem;
    background: #f3f4f6;
    border: 1px solid #e5e7eb;
    border-radius: 4px;
    font-size: 0.75rem;
    font-family: var(--font-mono);
    color: #6b7280;
    cursor: pointer;
}

.quick-tag-btn:hover {
    background: #e5e7eb;
}

.md-preview {
    padding: 0.75rem;
    background: #f9fafb;
    border: 1px solid #e5e7eb;
    border-radius: 6px;
    min-height: 120px;
    cursor: text;
}

.dialog-footer {
    display: flex;
    gap: 0.75rem;
    justify-content: flex-end;
}

.btn {
    padding: 0.5rem 1.25rem;
    border: none;
    border-radius: 6px;
    font-size: 0.85rem;
    font-family: var(--font-mono);
    cursor: pointer;
    transition: all 0.2s ease;
}

.btn-primary {
    background: #2563eb;
    color: white;
}

.btn-primary:hover {
    background: #1d4ed8;
}

.btn-secondary {
    background: #f3f4f6;
    color: #4b5563;
    border: 1px solid #e5e7eb;
}

.btn-secondary:hover {
    background: #e5e7eb;
}

.btn-danger {
    background: #dc2626;
    color: white;
}

.btn-danger:hover {
    background: #b91c1c;
}

/* Element Plus dialog override */
:deep(.el-dialog) {
    border-radius: 8px;
}

:deep(.el-dialog__header) {
    padding: 1rem 1.25rem;
    border-bottom: 1px solid #e5e7eb;
}

:deep(.el-dialog__title) {
    font-family: var(--font-mono);
    font-weight: 600;
}

:deep(.el-dialog__body) {
    padding: 1.25rem;
}

:deep(.el-dialog__footer) {
    padding: 1rem 1.25rem;
    border-top: 1px solid #e5e7eb;
}

@media (max-width: 640px) {
    .note-container {
        padding: 1.5rem 1rem;
    }

    .tag-filter {
        flex-direction: column;
        align-items: flex-start;
    }

    .filter-label {
        padding-top: 0;
    }

    .note-dialog:deep(.el-dialog) {
        width: 95% !important;
        margin: 5vh auto !important;
    }

    .dialog-content {
        max-height: 60vh;
        overflow-y: auto;
    }

    .form-textarea {
        min-height: 80px;
    }
}
</style>
