<template>
    <div class="default-main doc-md-page" v-loading="loading">
        <el-card shadow="never">
            <template #header>
                <div class="doc-md-header">
                    <span class="doc-md-title">{{ pageTitle }}</span>
                    <el-button type="primary" :loading="downloading" @click="onDownload">{{ t('docs36ziMobileApi.download') }}</el-button>
                </div>
            </template>
            <div v-if="loadError" class="color-danger">{{ loadError }}</div>
            <el-row v-else :gutter="16" class="doc-md-row">
                <el-col v-if="toc.length" :xs="24" :sm="8" :md="7" :lg="6" class="doc-md-nav-col">
                    <el-affix :offset="88">
                        <div class="doc-nav-panel">
                            <div class="doc-nav-title">{{ t('docs36ziMobileApi.nav_outline') }}</div>
                            <el-scrollbar max-height="calc(100vh - 200px)">
                                <ul class="doc-nav-list">
                                    <li
                                        v-for="item in toc"
                                        :key="item.id"
                                        class="doc-nav-item"
                                        :class="'doc-nav-level-' + item.level"
                                    >
                                        <a href="#" class="doc-nav-link" @click.prevent="scrollToHeading(item.id)">{{
                                            item.text
                                        }}</a>
                                    </li>
                                </ul>
                            </el-scrollbar>
                        </div>
                    </el-affix>
                </el-col>
                <el-col :xs="24" :sm="toc.length ? 16 : 24" :md="toc.length ? 17 : 24" :lg="toc.length ? 18 : 24">
                    <div ref="bodyRef" class="ba-markdown doc-md-body" v-html="html"></div>
                </el-col>
            </el-row>
        </el-card>
    </div>
</template>

<script setup lang="ts">
import { marked } from 'marked'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { getUrl } from '/@/utils/axios'
import createAxios from '/@/utils/axios'
import { useAdminInfo } from '/@/stores/adminInfo'
import { useConfig } from '/@/stores/config'

const { t } = useI18n()
const adminInfo = useAdminInfo()
const config = useConfig()

const bodyRef = ref<HTMLElement | null>(null)
const loading = ref(true)
const downloading = ref(false)
const markdown = ref('')
const loadError = ref('')
const downloadFilename = ref('36字花-移动端接口设计草案.md')

marked.use({ breaks: true })

interface TocItem {
    level: number
    text: string
    id: string
}

const toc = computed((): TocItem[] => {
    const md = markdown.value
    if (!md) {
        return []
    }
    const lines = md.split(/\r?\n/)
    const out: TocItem[] = []
    let idx = 0
    for (const line of lines) {
        const trimmed = line.trim()
        const m = /^(#{1,4})\s+(.+)$/.exec(trimmed)
        if (!m) {
            continue
        }
        const level = m[1].length
        let text = m[2].trim()
        text = text.replace(/\s+#+\s*$/, '').trim()
        out.push({ level, text, id: 'md-nav-' + String(idx) })
        idx += 1
    }
    return out
})

const html = computed(() => {
    if (!markdown.value) {
        return ''
    }
    return String(marked.parse(markdown.value))
})

const pageTitle = computed(() => t('docs36ziMobileApi.doc_title'))

function applyHeadingIds() {
    const root = bodyRef.value
    const items = toc.value
    if (!root || !items.length) {
        return
    }
    const hs = root.querySelectorAll('h1, h2, h3, h4')
    const n = Math.min(items.length, hs.length)
    for (let i = 0; i < n; i++) {
        const el = hs.item(i)
        el.id = items[i].id
    }
}

watch(
    () => [html.value, markdown.value],
    () => {
        void nextTick(() => {
            applyHeadingIds()
        })
    }
)

async function loadContent() {
    loading.value = true
    loadError.value = ''
    try {
        const res = await createAxios(
            {
                url: '/admin/docs.Doc36ZiHuaMobileApi/content',
                method: 'get',
            },
            { loading: false }
        )
        if (res.code === 1 && res.data && typeof res.data.markdown === 'string') {
            markdown.value = res.data.markdown
            if (typeof res.data.filename === 'string' && res.data.filename) {
                downloadFilename.value = res.data.filename
            }
        } else {
            loadError.value = res.msg || t('docs36ziMobileApi.load_fail')
        }
    } catch {
        loadError.value = t('docs36ziMobileApi.load_fail')
    } finally {
        loading.value = false
        void nextTick(() => {
            applyHeadingIds()
        })
    }
}

function scrollToHeading(id: string) {
    const el = document.getElementById(id)
    if (el) {
        el.scrollIntoView({ behavior: 'smooth', block: 'start' })
    }
}

function triggerBlobDownload(blob: Blob, filename: string) {
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = filename
    a.style.display = 'none'
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(url)
}

async function onDownload() {
    downloading.value = true
    try {
        const token = adminInfo.getToken()
        const fullUrl = getUrl() + '/admin/docs.Doc36ZiHuaMobileApi/download'
        const response = await fetch(fullUrl, {
            method: 'GET',
            headers: {
                batoken: token,
                server: 'true',
                'think-lang': config.lang.defaultLang,
            },
        })
        if (!response.ok) {
            loadError.value = t('docs36ziMobileApi.load_fail')
            return
        }
        const ctype = response.headers.get('content-type') || ''
        if (ctype.includes('application/json')) {
            const j = await response.json()
            loadError.value = typeof j.msg === 'string' && j.msg ? j.msg : t('docs36ziMobileApi.load_fail')
            return
        }
        const blob = await response.blob()
        triggerBlobDownload(blob, downloadFilename.value)
    } catch {
        loadError.value = t('docs36ziMobileApi.load_fail')
    } finally {
        downloading.value = false
    }
}

onMounted(() => {
    void loadContent()
})
</script>

<style scoped lang="scss">
.doc-md-page {
    .doc-md-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        flex-wrap: wrap;
    }
    .doc-md-title {
        font-weight: 600;
    }
    .doc-md-row {
        align-items: flex-start;
    }
    .doc-md-nav-col {
        margin-bottom: 16px;
        @media (min-width: 768px) {
            margin-bottom: 0;
        }
    }
    .doc-nav-panel {
        padding: 12px 12px 8px;
        border: 1px solid var(--el-border-color-lighter);
        border-radius: var(--el-border-radius-base);
        background: var(--ba-bg-color-overlay);
    }
    .doc-nav-title {
        font-weight: 600;
        font-size: 14px;
        margin-bottom: 8px;
        padding-bottom: 8px;
        border-bottom: 1px solid var(--el-border-color-lighter);
    }
    .doc-nav-list {
        list-style: none;
        margin: 0;
        padding: 0 0 8px;
    }
    .doc-nav-item {
        margin: 0;
        padding: 4px 0;
        line-height: 1.35;
    }
    .doc-nav-level-1 {
        padding-left: 0;
    }
    .doc-nav-level-2 {
        padding-left: 10px;
    }
    .doc-nav-level-3 {
        padding-left: 20px;
    }
    .doc-nav-level-4 {
        padding-left: 30px;
    }
    .doc-nav-link {
        color: var(--el-text-color-regular);
        text-decoration: none;
        font-size: 13px;
        word-break: break-word;
        cursor: pointer;
        &:hover {
            color: var(--el-color-primary);
        }
    }
    .doc-md-body {
        max-width: 1100px;
    }
}
</style>
