<script setup lang="ts">
import { NButton, NDataTable, NSpin, type DataTableColumns } from 'naive-ui'
import { computed, h, ref } from 'vue'

import { listInvoices, listPaymentMethods } from '@/api/billing'
import { ApiError } from '@/api/request'
import EmptyCard from '@/components/EmptyCard.vue'
import StatusBadge from '@/components/StatusBadge.vue'
import InvoiceDetailModal from '@/components/tenant/InvoiceDetailModal.vue'
import InvoicePayModal from '@/components/tenant/InvoicePayModal.vue'
import InvoicePdfModal from '@/components/tenant/InvoicePdfModal.vue'
import PaymentMethodsModal from '@/components/tenant/PaymentMethodsModal.vue'
import { invoiceDate, invoiceStatusMeta } from '@/components/tenant/invoiceStatus'
import { useAsync } from '@/composables/useAsync'
import { useToast } from '@/composables/useToast'
import { fmtMoney } from '@/composables/useFormat'
import type { Invoice } from '@/types/api'

const props = defineProps<{ cfgId: number }>()
const message = useToast()

// ---- 按年懒加载:初始拉当年(空则自动往前探),底部按钮逐年加载,连空 2 年停 ----
const thisYear = new Date().getFullYear()
const EMPTY_STOP = 2

const rows = ref<Invoice[]>([])
/** 下一个待加载的自然年 */
const nextYear = ref(thisYear)
const exhausted = ref(false)
const initialLoading = ref(true)
const loadingMore = ref(false)
/** 无 osp-gateway 权限时整 tab 转受限卡,不当作普通错误 */
const forbidden = ref(false)
/** 组织被 OCI 账单平台排除(巴西等本地结算体系注册地),账单能力整体不适用 */
const excluded = ref(false)
const loadError = ref('')
let emptyStreak = 0

/** 拉一个自然年并入列表 */
async function loadYear(year: number) {
  const batch = await listInvoices(props.cfgId, year)
  nextYear.value = year - 1
  if (batch.length) {
    emptyStreak = 0
    rows.value = [...rows.value, ...batch]
  } else if (++emptyStreak >= EMPTY_STOP) {
    exhausted.value = true
  }
}

async function init() {
  initialLoading.value = true
  forbidden.value = false
  excluded.value = false
  loadError.value = ''
  rows.value = []
  nextYear.value = thisYear
  exhausted.value = false
  emptyStreak = 0
  try {
    // 自动往前探,直到有数据或连空到停
    while (!exhausted.value && !rows.value.length) await loadYear(nextYear.value)
  } catch (e) {
    // ociCode 判定先于 403:排除类错误可能也以 403 状态返回,不能被权限分支抢走
    if (e instanceof ApiError && e.ociCode === 'OrganizationExclusionError') excluded.value = true
    else if (e instanceof ApiError && e.status === 403) forbidden.value = true
    else loadError.value = e instanceof Error ? e.message : String(e)
  } finally {
    initialLoading.value = false
  }
}
void init()

async function loadMore() {
  loadingMore.value = true
  try {
    await loadYear(nextYear.value)
  } catch (e) {
    message.error(e instanceof Error ? e.message : '加载失败')
  } finally {
    loadingMore.value = false
  }
}

/** 付款后静默重拉已加载的年份区间,不闪加载态 */
async function refreshSilent() {
  const years: number[] = []
  for (let y = thisYear; y > nextYear.value; y--) years.push(y)
  try {
    const batches = await Promise.all(years.map((y) => listInvoices(props.cfgId, y)))
    rows.value = batches.flat()
  } catch {
    /* 静默失败,保留现有数据 */
  }
}

/** 非 PAYG 计划(如年度合约/内部账号)没有自助付款方式,单独提示而非报错 */
const methodsUnsupported = ref(false)
/** 付款方式受同一 policy 管制,其余失败静默置空(受限卡已说明原因) */
const methods = useAsync(() =>
  listPaymentMethods(props.cfgId).catch((e) => {
    if (e instanceof ApiError && e.ociCode === 'InvalidPlanType') methodsUnsupported.value = true
    return []
  }),
)

// ---- 汇总 ----
/** API 返回序不保证,统一按开票时间倒序 */
const list = computed(() =>
  [...rows.value].sort((a, b) => (b.timeInvoice ?? '').localeCompare(a.timeInvoice ?? '')),
)
const currency = computed(() => list.value[0]?.currency || 'USD')
/** 未付余额只计还可操作的欠款;「处理中」已提交扣款,不再计入 */
const unpaidDue = computed(() =>
  list.value
    .filter((i) => !i.isPaid && (i.status === 'OPEN' || i.status === 'PAST_DUE'))
    .reduce((s, i) => s + i.amountDue, 0),
)
const yearTotal = computed(() =>
  list.value
    .filter((i) => (i.timeInvoice ?? '').startsWith(String(thisYear)))
    .reduce((s, i) => s + i.amount, 0),
)
const lastPaid = computed(() => list.value.find((i) => i.isPaid) ?? null)
const methodBrief = computed(() => {
  if (methodsUnsupported.value) return '不适用（非 PAYG）'
  const m = (methods.data.value ?? [])[0]
  if (!m) return '未登记'
  return m.method === 'CREDIT_CARD' ? `${m.cardType || '银行卡'} •••• ${m.lastDigits}` : 'PayPal'
})

// ---- 弹窗 ----
const detailInv = ref<Invoice | null>(null)
const showDetail = ref(false)
const payInv = ref<Invoice | null>(null)
const showPay = ref(false)
const pdfInv = ref<Invoice | null>(null)
const showPdf = ref(false)
const showMethods = ref(false)

function openDetail(inv: Invoice) {
  detailInv.value = inv
  showDetail.value = true
}

function openPay(inv: Invoice) {
  payInv.value = inv
  showPay.value = true
}

function openPdf(inv: Invoice) {
  pdfInv.value = inv
  showPdf.value = true
}

/** 明细弹窗里点付款:先关明细再开付款确认 */
function payFromDetail(inv: Invoice) {
  showDetail.value = false
  openPay(inv)
}

const columns = computed<DataTableColumns<Invoice>>(() => [
  {
    title: '发票号',
    key: 'number',
    render: (r) =>
      h('div', [
        h('div', { class: 'text-[13px] font-medium tabular-nums' }, r.number || r.id),
        h('div', { class: 'mt-0.5 text-xs text-ink-3' }, r.type || 'USAGE'),
      ]),
  },
  { title: '开票日期', key: 'timeInvoice', width: 110, render: (r) => invoiceDate(r.timeInvoice) },
  {
    title: '到期日期',
    key: 'timeDue',
    width: 110,
    render: (r) =>
      h('span', { class: r.status === 'PAST_DUE' ? 'text-err' : '' }, invoiceDate(r.timeDue)),
  },
  {
    title: '金额',
    key: 'amount',
    width: 110,
    align: 'right',
    render: (r) => h('span', { class: 'tabular-nums' }, fmtMoney(r.amount, r.currency)),
  },
  {
    title: '状态',
    key: 'status',
    width: 100,
    render: (r) => {
      const m = invoiceStatusMeta(r)
      return h(StatusBadge, { kind: m.kind, label: m.label })
    },
  },
  {
    title: '操作',
    key: 'actions',
    width: 170,
    align: 'right',
    render: (r) =>
      h('div', { class: 'flex justify-end gap-1' }, [
        r.isPayable && !r.isPaid
          ? h(
              NButton,
              { size: 'tiny', type: 'primary', onClick: (e: MouseEvent) => { e.stopPropagation(); openPay(r) } },
              { default: () => '付款' },
            )
          : null,
        h(
          NButton,
          { size: 'tiny', quaternary: true, onClick: (e: MouseEvent) => { e.stopPropagation(); openDetail(r) } },
          { default: () => '明细' },
        ),
        h(
          NButton,
          { size: 'tiny', quaternary: true, onClick: (e: MouseEvent) => { e.stopPropagation(); openPdf(r) } },
          { default: () => 'PDF' },
        ),
      ]),
  },
])

const rowProps = (row: Invoice) => ({
  style: 'cursor: pointer',
  onClick: () => openDetail(row),
})
</script>

<template>
  <div class="flex flex-col gap-4">
    <div v-if="initialLoading" class="panel flex items-center justify-center py-16">
      <NSpin size="small" />
    </div>

    <div v-else-if="forbidden" class="panel">
      <EmptyCard
        title="无账单访问权限"
        note="当前 API Key 所属用户无 OSP Gateway 权限;需要租户管理员授予 policy:Allow group Administrators to manage osp-gateway in tenancy"
      />
    </div>

    <div v-else-if="excluded" class="panel">
      <EmptyCard
        title="账单服务不适用于该租户"
        note="OCI 账单平台(OSP Gateway)排除了该租户所属组织(OrganizationExclusionError),常见于巴西等采用当地结算体系的注册地;发票请前往 Oracle 官方控制台或当地结算渠道查询"
      />
    </div>

    <div v-else-if="loadError" class="panel flex items-center gap-2 px-4.5 py-3 text-xs text-err">
      <span class="min-w-0 flex-1 truncate" :title="loadError">{{ loadError }}</span>
      <NButton size="tiny" quaternary @click="init">重试</NButton>
    </div>

    <div v-else-if="!list.length" class="panel">
      <EmptyCard
        title="暂无发票"
        note="Free Tier / Always Free 用量不产生发票;升级为付费账户后,月度发票将在此展示"
      />
    </div>

    <template v-else>
      <div class="grid grid-cols-4 gap-3 max-md:grid-cols-2">
        <div class="panel px-4 py-3">
          <div class="text-xs text-ink-3">未付余额</div>
          <div class="mt-1 text-[20px] font-semibold text-accent tabular-nums">
            {{ fmtMoney(unpaidDue, currency) }}
          </div>
        </div>
        <div class="panel px-4 py-3">
          <div class="text-xs text-ink-3">本年已开票</div>
          <div class="mt-1 text-[20px] font-semibold tabular-nums">
            {{ fmtMoney(yearTotal, currency) }}
          </div>
        </div>
        <div class="panel px-4 py-3">
          <div class="text-xs text-ink-3">上期已付发票</div>
          <div class="mt-1 text-[20px] font-semibold tabular-nums">
            {{ lastPaid ? fmtMoney(lastPaid.amount, lastPaid.currency) : '—' }}
          </div>
        </div>
        <div
          class="panel cursor-pointer px-4 py-3 transition-colors hover:border-accent/45"
          title="查看付款方式"
          @click="showMethods = true"
        >
          <div class="flex items-center justify-between text-xs text-ink-3">
            <span>默认付款方式</span>
            <span class="text-ink-3">›</span>
          </div>
          <div class="mt-1 truncate text-[15px] font-semibold">{{ methodBrief }}</div>
        </div>
      </div>

      <div class="panel">
        <div class="flex items-center gap-2.5 border-b border-line-soft px-4.5 py-3.5">
          <div class="text-sm font-semibold">发票</div>
          <div class="text-xs text-ink-3">已加载 {{ list.length }} 张,点击行查看费用明细</div>
          <div class="flex-1" />
          <NButton size="tiny" quaternary @click="init">刷新</NButton>
        </div>
        <NDataTable
          :columns="columns"
          :data="list"
          :bordered="false"
          :bottom-bordered="false"
          :row-props="rowProps"
          :row-key="(r: Invoice) => r.id"
          :scroll-x="720"
          size="small"
        />
        <div class="flex justify-center border-t border-line-soft py-2">
          <NButton
            v-if="!exhausted"
            size="tiny"
            quaternary
            :loading="loadingMore"
            @click="loadMore"
          >
            加载 {{ nextYear }} 年发票
          </NButton>
          <span v-else class="text-xs text-ink-3">已加载全部发票</span>
        </div>
      </div>
    </template>

    <InvoiceDetailModal
      v-model:show="showDetail"
      :cfg-id="cfgId"
      :invoice="detailInv"
      @pay="payFromDetail"
    />
    <InvoicePayModal
      v-model:show="showPay"
      :cfg-id="cfgId"
      :invoice="payInv"
      :methods="methods.data.value"
      @paid="refreshSilent"
    />
    <InvoicePdfModal v-model:show="showPdf" :cfg-id="cfgId" :invoice="pdfInv" />
    <PaymentMethodsModal
      v-model:show="showMethods"
      :methods="methods.data.value"
      :loading="methods.loading.value"
      :unsupported="methodsUnsupported"
    />
  </div>
</template>
