<template>
    <div class="mapping-page">
        <header class="page-header">
            <h1>洛阳药房药材映射</h1>
            <el-button
                v-if="status.sync_enabled"
                v-perms="['pharmacy.medicineMapping/sync']"
                type="primary"
                :icon="Refresh"
                :loading="syncing"
                @click="handleSync"
            >
                增量同步
            </el-button>
        </header>

        <section class="status-strip" v-loading="statusLoading">
            <div class="status-item">
                <span>目录</span>
                <strong>{{ status.catalog_active }} / {{ status.catalog_total }}</strong>
                <small>启用 / 总数</small>
            </div>
            <div class="status-item warning">
                <span>未映射本地药材</span>
                <strong>{{ status.unmapped_local }}</strong>
                <small>仅统计启用药材</small>
            </div>
            <div class="status-item">
                <span>同步游标</span>
                <strong>{{ status.cursor }}</strong>
                <small>{{ formatTime(status.last_success_time) || '尚未成功同步' }}</small>
            </div>
            <div class="status-item" :class="{ danger: !!status.last_error_summary }">
                <span>最近同步</span>
                <strong>{{
                    status.is_syncing ? '进行中' : status.last_error_summary ? '失败' : '正常'
                }}</strong>
                <small :title="status.last_error_summary">
                    {{
                        status.last_error_summary ||
                        formatTime(status.last_failure_time) ||
                        '无失败记录'
                    }}
                </small>
            </div>
        </section>

        <el-form class="filter-bar" inline @submit.prevent>
            <el-form-item label="本地药材">
                <el-input
                    v-model="query.local_name"
                    clearable
                    placeholder="名称"
                    :prefix-icon="Search"
                    @keyup.enter="search"
                />
            </el-form-item>
            <el-form-item label="远端目录">
                <el-input
                    v-model="query.remote_keyword"
                    clearable
                    placeholder="名称或编码"
                    :prefix-icon="Search"
                    @keyup.enter="search"
                />
            </el-form-item>
            <el-form-item label="映射状态">
                <el-select
                    v-model="query.mapping_status"
                    clearable
                    placeholder="全部"
                    style="width: 150px"
                >
                    <el-option label="已映射" value="mapped" />
                    <el-option label="未映射" value="unmapped" />
                    <el-option label="远端失效" value="invalid" />
                </el-select>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" :icon="Search" @click="search">查询</el-button>
                <el-button @click="resetFilters">重置</el-button>
            </el-form-item>
        </el-form>

        <div class="table-wrap">
            <el-table v-loading="loading" :data="rows" border stripe table-layout="fixed">
                <el-table-column label="本地药材" min-width="190" fixed="left">
                    <template #default="{ row }">
                        <div class="medicine-name">{{ row.local_name }}</div>
                        <div class="subline">
                            ID {{ row.local_medicine_id }} · {{ row.local_unit || '-' }}
                        </div>
                    </template>
                </el-table-column>
                <el-table-column label="本地状态" width="90" align="center">
                    <template #default="{ row }">
                        <el-tag :type="row.local_status === 1 ? 'success' : 'info'" size="small">
                            {{ row.local_status === 1 ? '启用' : '停用' }}
                        </el-tag>
                    </template>
                </el-table-column>
                <el-table-column label="映射状态" width="105" align="center">
                    <template #default="{ row }">
                        <el-tag :type="mappingTag(row.mapping_status)" size="small">
                            {{ mappingText(row.mapping_status) }}
                        </el-tag>
                    </template>
                </el-table-column>
                <el-table-column label="洛阳药房目录" min-width="250">
                    <template #default="{ row }">
                        <template v-if="row.medicine_code">
                            <div class="medicine-name">{{ row.remote_name || '目录项不可用' }}</div>
                            <div class="subline code">{{ row.medicine_code }}</div>
                        </template>
                        <template v-else>
                            <div class="medicine-name">-</div>
                            <div class="subline">未映射</div>
                        </template>
                    </template>
                </el-table-column>
                <el-table-column label="品牌" min-width="130" show-overflow-tooltip>
                    <template #default="{ row }">
                        {{ row.mapping_status === 0 ? '-' : row.remote_brand || '-' }}
                    </template>
                </el-table-column>
                <el-table-column label="单位" width="80" align="center">
                    <template #default="{ row }">{{ row.remote_unit || '-' }}</template>
                </el-table-column>
                <el-table-column label="价格" width="155" align="right">
                    <template #default="{ row }">
                        <template v-if="row.mapping_status !== 0">
                            <div>结算 ¥{{ formatPrice(row.settlement_price) }}</div>
                            <div class="subline">零售 ¥{{ formatPrice(row.retail_price) }}</div>
                        </template>
                        <template v-else>
                            <div>-</div>
                            <div class="subline">未映射</div>
                        </template>
                    </template>
                </el-table-column>
                <el-table-column label="版本 / 状态" width="130" align="center">
                    <template #default="{ row }">
                        <template v-if="row.medicine_code">
                            <div>v{{ row.catalog_version || 0 }}</div>
                            <div class="subline">
                                {{
                                    row.remote_status === 1 && row.remote_deleted !== 1
                                        ? '远端启用'
                                        : '远端停用'
                                }}
                            </div>
                        </template>
                        <template v-else>
                            <div>-</div>
                            <div class="subline">未映射</div>
                        </template>
                    </template>
                </el-table-column>
                <el-table-column label="操作" width="170" fixed="right" align="center">
                    <template #default="{ row }">
                        <el-button
                            v-perms="['pharmacy.medicineMapping/save']"
                            type="primary"
                            link
                            :icon="Link"
                            :disabled="row.local_status !== 1"
                            @click="openMapping(row)"
                        >
                            {{ row.mapping_status === 1 ? '更换' : '映射' }}
                        </el-button>
                        <el-button
                            v-if="row.mapping_status === 1 || row.mapping_status === 2"
                            v-perms="['pharmacy.medicineMapping/unlink']"
                            type="danger"
                            link
                            :icon="CloseBold"
                            @click="handleUnlink(row)"
                        >
                            解除
                        </el-button>
                    </template>
                </el-table-column>
            </el-table>
        </div>

        <div class="pagination-wrap">
            <el-pagination
                v-model:current-page="query.page_no"
                v-model:page-size="query.page_size"
                :total="total"
                :page-sizes="[20, 50, 100]"
                layout="total, sizes, prev, pager, next, jumper"
                @size-change="loadRows"
                @current-change="loadRows"
            />
        </div>

        <el-dialog
            v-model="dialogVisible"
            :title="`${editingRow?.mapping_status === 1 ? '更换' : '建立'}药材映射`"
            width="min(560px, calc(100vw - 32px))"
            destroy-on-close
        >
            <div class="local-summary">
                <span>本地药材</span>
                <strong>{{ editingRow?.local_name }}</strong>
                <small
                    >ID {{ editingRow?.local_medicine_id }} ·
                    {{ editingRow?.local_unit || '-' }}</small
                >
            </div>
            <el-form label-position="top" class="mapping-form">
                <el-form-item label="洛阳药房药材">
                    <el-select
                        v-model="selectedCode"
                        filterable
                        remote
                        reserve-keyword
                        clearable
                        :remote-method="searchCatalog"
                        :loading="catalogLoading"
                        placeholder="输入药材名称或编码检索"
                        style="width: 100%"
                    >
                        <el-option
                            v-for="option in catalogOptions"
                            :key="option.medicine_code"
                            :label="`${option.name} · ${option.medicine_code}`"
                            :value="option.medicine_code"
                        >
                            <div class="option-row">
                                <span>{{ option.name }}</span>
                                <small
                                    >{{ option.medicine_code }} · {{ option.brand || '无品牌' }} ·
                                    {{ option.unit }}</small
                                >
                            </div>
                        </el-option>
                    </el-select>
                </el-form-item>
            </el-form>
            <template #footer>
                <el-button @click="dialogVisible = false">取消</el-button>
                <el-button
                    type="primary"
                    :loading="saving"
                    :disabled="!selectedCode"
                    @click="saveMapping"
                >
                    保存映射
                </el-button>
            </template>
        </el-dialog>
    </div>
</template>

<script setup lang="ts" name="pharmacyMedicineMapping">
import { onMounted, reactive, ref } from 'vue'
import { CloseBold, Link, Refresh, Search } from '@element-plus/icons-vue'
import { ElMessageBox } from 'element-plus'
import feedback from '@/utils/feedback'
import { createLatestRequestGuard } from './latest-request.mjs'
import {
    medicineCatalogOptions,
    medicineCatalogSync,
    medicineMappingLists,
    medicineMappingSave,
    medicineMappingStatus,
    medicineMappingUnlink,
    type CatalogOption,
    type MedicineMappingQuery,
    type MedicineMappingRow,
    type PharmacySyncResult,
    type PharmacySyncStatus
} from '@/api/pharmacy'

const emptyStatus = (): PharmacySyncStatus => ({
    sync_enabled: false,
    cursor: 0,
    last_success_time: 0,
    last_failure_time: 0,
    last_error_summary: '',
    is_syncing: false,
    catalog_total: 0,
    catalog_active: 0,
    unmapped_local: 0
})

const loading = ref(false)
const statusLoading = ref(false)
const syncing = ref(false)
const saving = ref(false)
const catalogLoading = ref(false)
const rows = ref<MedicineMappingRow[]>([])
const total = ref(0)
const status = ref<PharmacySyncStatus>(emptyStatus())
const query = reactive<MedicineMappingQuery>({
    page_no: 1,
    page_size: 20,
    local_name: '',
    remote_keyword: '',
    mapping_status: ''
})

const dialogVisible = ref(false)
const editingRow = ref<MedicineMappingRow | null>(null)
const selectedCode = ref('')
const catalogOptions = ref<CatalogOption[]>([])
let catalogTimer: number | undefined
const listRequests = createLatestRequestGuard<MedicineMappingQuery>()
const catalogRequests = createLatestRequestGuard<{ keyword: string; localMedicineId: number }>()
const statusRequests = createLatestRequestGuard()

const formatPrice = (value: string | number | null | undefined) => {
    const number = Number(value || 0)
    return Number.isFinite(number) ? number.toFixed(2) : '0.00'
}

const formatTime = (timestamp: number) => {
    if (!timestamp) return ''
    return new Date(timestamp * 1000).toLocaleString('zh-CN', { hour12: false })
}

const mappingText = (value: number) => {
    if (value === 1) return '已映射'
    if (value === 2) return '远端失效'
    return '未映射'
}

const mappingTag = (value: number): 'success' | 'warning' | 'info' => {
    if (value === 1) return 'success'
    if (value === 2) return 'warning'
    return 'info'
}

const loadRows = async () => {
    const ticket = listRequests.next({ ...query })
    loading.value = true
    try {
        const response = await medicineMappingLists(ticket.snapshot)
        if (listRequests.isLatest(ticket)) {
            rows.value = response.lists || []
            total.value = response.count || 0
        }
    } finally {
        if (listRequests.isLatest(ticket)) {
            loading.value = false
        }
    }
}

const loadStatus = async () => {
    const ticket = statusRequests.next(undefined)
    statusLoading.value = true
    try {
        const nextStatus = await medicineMappingStatus()
        if (statusRequests.isLatest(ticket)) {
            status.value = nextStatus
        }
    } finally {
        if (statusRequests.isLatest(ticket)) {
            statusLoading.value = false
        }
    }
}

const search = () => {
    query.page_no = 1
    void loadRows()
}

const resetFilters = () => {
    query.local_name = ''
    query.remote_keyword = ''
    query.mapping_status = ''
    search()
}

const handleSync = async () => {
    syncing.value = true
    try {
        const result = (await medicineCatalogSync()) as PharmacySyncResult
        feedback.msgSuccess(
            `同步完成：拉取 ${result.pulled}，新增 ${result.created}，更新 ${result.updated}，停用 ${result.deactivated}`
        )
        await Promise.all([loadRows(), loadStatus()])
    } finally {
        syncing.value = false
    }
}

const searchCatalogNow = async (
    keyword: string,
    localMedicineId = Number(editingRow.value?.local_medicine_id || 0)
) => {
    const ticket = catalogRequests.next({ keyword: keyword.trim(), localMedicineId })
    catalogLoading.value = true
    try {
        const options = await medicineCatalogOptions({ keyword: ticket.snapshot.keyword, limit: 30 })
        if (
            catalogRequests.isLatest(ticket) &&
            Number(editingRow.value?.local_medicine_id || 0) === ticket.snapshot.localMedicineId
        ) {
            catalogOptions.value = options
        }
    } finally {
        if (catalogRequests.isLatest(ticket)) {
            catalogLoading.value = false
        }
    }
    return ticket
}

const searchCatalog = (keyword: string) => {
    if (catalogTimer) window.clearTimeout(catalogTimer)
    catalogRequests.invalidate()
    catalogTimer = window.setTimeout(() => void searchCatalogNow(keyword), 250)
}

const openMapping = async (row: MedicineMappingRow) => {
    if (catalogTimer) window.clearTimeout(catalogTimer)
    catalogRequests.invalidate()
    const rowSnapshot = { ...row }
    const localMedicineId = Number(rowSnapshot.local_medicine_id)
    editingRow.value = rowSnapshot
    selectedCode.value = rowSnapshot.mapping_status === 1 ? rowSnapshot.medicine_code || '' : ''
    catalogOptions.value = []
    dialogVisible.value = true
    const initialTicket = await searchCatalogNow(rowSnapshot.local_name, localMedicineId)
    if (
        !catalogRequests.isLatest(initialTicket) ||
        Number(editingRow.value?.local_medicine_id || 0) !== localMedicineId
    )
        return
    if (
        selectedCode.value &&
        !catalogOptions.value.some((item) => item.medicine_code === selectedCode.value)
    ) {
        await searchCatalogNow(selectedCode.value, localMedicineId)
    }
}

const saveMapping = async () => {
    if (!editingRow.value || !selectedCode.value) return
    saving.value = true
    try {
        await medicineMappingSave({
            local_medicine_id: editingRow.value.local_medicine_id,
            medicine_code: selectedCode.value
        })
        dialogVisible.value = false
        await Promise.all([loadRows(), loadStatus()])
    } finally {
        saving.value = false
    }
}

const handleUnlink = async (row: MedicineMappingRow) => {
    await ElMessageBox.confirm(
        `确认解除“${row.local_name}”与 ${row.medicine_code} 的映射？`,
        '解除映射',
        {
            type: 'warning',
            confirmButtonText: '解除',
            cancelButtonText: '取消'
        }
    )
    await medicineMappingUnlink({ local_medicine_id: row.local_medicine_id })
    await Promise.all([loadRows(), loadStatus()])
}

onMounted(() => {
    void Promise.all([loadRows(), loadStatus()])
})
</script>

<style scoped>
.mapping-page {
    min-width: 0;
    padding: 16px;
    color: var(--el-text-color-primary);
}

.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 16px;
}

.page-header h1 {
    margin: 0;
    font-size: 20px;
    line-height: 28px;
    letter-spacing: 0;
}

.status-strip {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    min-height: 88px;
    margin-bottom: 16px;
    border: 1px solid var(--el-border-color-light);
    background: var(--el-bg-color);
}

.status-item {
    min-width: 0;
    padding: 14px 16px;
    border-right: 1px solid var(--el-border-color-lighter);
}

.status-item:last-child {
    border-right: 0;
}

.status-item span,
.status-item small {
    display: block;
    overflow: hidden;
    color: var(--el-text-color-secondary);
    font-size: 12px;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.status-item strong {
    display: block;
    margin: 5px 0 2px;
    font-size: 18px;
    line-height: 24px;
}

.status-item.warning strong {
    color: var(--el-color-warning-dark-2);
}

.status-item.danger strong,
.status-item.danger small {
    color: var(--el-color-danger);
}

.filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 0 8px;
    padding: 14px 16px 0;
    border: 1px solid var(--el-border-color-light);
    border-bottom: 0;
    background: var(--el-fill-color-extra-light);
}

.filter-bar :deep(.el-input) {
    width: 190px;
}

.table-wrap {
    min-width: 0;
    overflow-x: auto;
}

.table-wrap :deep(.el-table) {
    min-width: 1220px;
}

.medicine-name {
    overflow: hidden;
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.subline,
.muted {
    margin-top: 3px;
    color: var(--el-text-color-secondary);
    font-size: 12px;
}

.code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}

.pagination-wrap {
    display: flex;
    justify-content: flex-end;
    overflow-x: auto;
    padding-top: 16px;
}

.local-summary {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 3px 12px;
    padding: 12px 14px;
    border-left: 3px solid var(--el-color-primary);
    background: var(--el-fill-color-light);
}

.local-summary span,
.local-summary small {
    color: var(--el-text-color-secondary);
    font-size: 12px;
}

.local-summary small {
    grid-column: 2;
}

.mapping-form {
    margin-top: 18px;
}

.option-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    width: 100%;
}

.option-row small {
    overflow: hidden;
    color: var(--el-text-color-secondary);
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (max-width: 900px) {
    .status-strip {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .status-item:nth-child(2) {
        border-right: 0;
    }

    .status-item:nth-child(-n + 2) {
        border-bottom: 1px solid var(--el-border-color-lighter);
    }
}

@media (max-width: 600px) {
    .mapping-page {
        padding: 12px;
    }

    .page-header {
        align-items: stretch;
        flex-direction: column;
    }

    .page-header .el-button {
        width: 100%;
    }

    .status-strip {
        grid-template-columns: 1fr;
    }

    .status-item,
    .status-item:nth-child(2) {
        border-right: 0;
        border-bottom: 1px solid var(--el-border-color-lighter);
    }

    .status-item:last-child {
        border-bottom: 0;
    }

    .filter-bar {
        display: block;
    }

    .filter-bar :deep(.el-form-item),
    .filter-bar :deep(.el-input),
    .filter-bar :deep(.el-select) {
        width: 100% !important;
    }

    .pagination-wrap {
        justify-content: flex-start;
    }
}
</style>
