import { useCallback, useEffect, useMemo, useState } from 'react'
import type { ReactNode } from 'react'
import { Alert, Card, Descriptions, Table, Tag, Typography } from 'antd'
import EndpointDoc, { CommonResponseDoc } from '../openapi/EndpointDoc'
import {
  commonResponseFields,
  endpoints,
  errorCodes,
  orderStatusTable,
  paymentStatusTable,
} from '../openapi/endpoints'
import { endpointSections, type EndpointSpec } from '../openapi/types'

const { Paragraph, Text } = Typography

const baseUrl =
  typeof window !== 'undefined'
    ? window.location.origin.replace(':5173', ':8080')
    : 'http://localhost:8080'

const deliveryModes = [
  {
    mode: 'Web 发货页',
    scene: '商户想要“只返回一个链接”，让用户直接打开平台页面填写 UID',
    interfaces: '/api/client/v1/orders/{order_no}/delivery-link',
  },
  {
    mode: '自建发货页',
    scene: '商户自己有前端页面，想在自己的系统里完成发货流程',
    interfaces:
      '/api/client/v1/orders/{order_no}/delivery\n/api/client/v1/orders/{order_no}/delivery/bind\n/api/client/v1/orders/{order_no}/delivery/submit',
  },
]

// ---------- TOC 目录构建 ----------
interface TocLeaf {
  id: string
  label: string
  level: 3 // 接口子小节（接口地址/请求方式/...）
}
interface TocItem {
  id: string
  label: string
  children?: TocLeaf[]
}
interface TocGroup {
  title: string
  items: TocItem[]
}

function buildToc(): TocGroup[] {
  return [
    {
      title: '开始',
      items: [
        { id: 'overview', label: '概述' },
        { id: 'auth', label: '鉴权与签名' },
      ],
    },
    {
      title: '接口',
      items: endpoints.map((ep) => ({
        id: `ep-${ep.key}`,
        label: ep.title,
        children: endpointSections.map((s) => ({
          id: `ep-${ep.key}-${s.id}`,
          label: s.label,
          level: 3 as const,
        })),
      })),
    },
    {
      title: '附录',
      items: [{ id: 'status', label: '状态说明' }],
    },
  ]
}

const toc = buildToc()

const pageItems = toc.flatMap((group) => group.items)
const pageIds = new Set(pageItems.map((item) => item.id))

function findPageId(id: string): string {
  if (pageIds.has(id)) return id
  for (const item of pageItems) {
    if (item.children?.some((child) => child.id === id)) return item.id
  }
  return 'overview'
}

function getInitialHash() {
  if (typeof window === 'undefined') return 'overview'
  return decodeURIComponent(window.location.hash.replace(/^#/, '')) || 'overview'
}

// ---------- 锚点跳转 ----------
function scrollToId(id: string) {
  const el = document.getElementById(id)
  if (!el) return
  const root = document.getElementById('docScroll')
  if (!root) return
  const top = root.scrollTop + el.getBoundingClientRect().top - root.getBoundingClientRect().top - 22
  root.scrollTo({ top, behavior: 'smooth' })
}

export default function OpenApiDocs() {
  const initialHash = useMemo(() => getInitialHash(), [])
  const [activePageId, setActivePageId] = useState(() => findPageId(initialHash))
  const [activeAnchorId, setActiveAnchorId] = useState(initialHash)
  const activeEndpoint = useMemo(
    () => endpoints.find((spec) => `ep-${spec.key}` === activePageId),
    [activePageId],
  )

  useEffect(() => {
    const syncHash = () => {
      const nextHash = getInitialHash()
      setActivePageId(findPageId(nextHash))
      setActiveAnchorId(nextHash)
    }
    window.addEventListener('hashchange', syncHash)
    return () => window.removeEventListener('hashchange', syncHash)
  }, [])

  useEffect(() => {
    const root = document.getElementById('docScroll')
    if (!root) return

    if (activeAnchorId !== activePageId) {
      requestAnimationFrame(() => scrollToId(activeAnchorId))
      return
    }
    root.scrollTo({ top: 0 })
  }, [activeAnchorId, activePageId])

  const activeIndex = useMemo(
    () => pageItems.findIndex((item) => item.id === activePageId),
    [activePageId],
  )

  const handleNav = useCallback((id: string) => {
    const pageId = findPageId(id)
    setActivePageId(pageId)
    setActiveAnchorId(id)
    if (typeof window !== 'undefined') {
      window.history.replaceState(null, '', `${window.location.pathname}${window.location.search}#${id}`)
    }
  }, [])

  const handleStep = useCallback(
    (direction: -1 | 1) => {
      const next = pageItems[activeIndex + direction]
      if (next) handleNav(next.id)
    },
    [activeIndex, handleNav],
  )

  return (
    <div className="api-docs">
      {/* 左侧目录 */}
      <aside className="api-docs__toc">
        <div className="api-docs__toc-tabs">
          <button
            className={`api-docs__toc-tab${activePageId !== 'auth' ? ' api-docs__toc-tab--active' : ''}`}
            type="button"
            onClick={() => handleNav('overview')}
          >
            开放 API
          </button>
          <button
            className={`api-docs__toc-tab${activePageId === 'auth' ? ' api-docs__toc-tab--active' : ''}`}
            type="button"
            onClick={() => handleNav('auth')}
          >
            API 密钥
          </button>
        </div>
        <nav className="api-docs__nav">
          {toc.map((group) => (
            <div className="api-docs__nav-group" key={group.title}>
              <div className="api-docs__nav-heading">{group.title}</div>
              {group.items.map((item) => {
                const isActive = activePageId === item.id
                return (
                  <div className="api-docs__nav-row" key={item.id}>
                    <a
                      className={`api-docs__nav-item${isActive ? ' api-docs__nav-item--active' : ''}`}
                      onClick={() => handleNav(item.id)}
                    >
                      {item.label}
                    </a>
                    {item.children?.length && isActive ? (
                      <div className="api-docs__nav-sub">
                        {item.children.map((c) => (
                          <a
                            key={c.id}
                            className={`api-docs__nav-sub-item${activeAnchorId === c.id ? ' api-docs__nav-sub-item--active' : ''}`}
                            onClick={() => handleNav(c.id)}
                          >
                            {c.label}
                          </a>
                        ))}
                      </div>
                    ) : null}
                  </div>
                )
              })}
            </div>
          ))}
        </nav>
      </aside>

      {/* 右侧文档 */}
      <div id="docScroll" className="api-docs__content">
        <div className="api-docs__inner">
          {activePageId === 'overview' ? <OverviewSection /> : null}
          {activePageId === 'auth' ? <AuthSection /> : null}
          {activeEndpoint ? <EndpointSection spec={activeEndpoint} /> : null}
          {activePageId === 'status' ? <StatusSection /> : null}
          <PageStepper
            currentIndex={activeIndex}
            items={pageItems}
            onStep={handleStep}
          />
        </div>
      </div>
    </div>
  )
}

function PageStepper({
  currentIndex,
  items,
  onStep,
}: {
  currentIndex: number
  items: TocItem[]
  onStep: (direction: -1 | 1) => void
}) {
  const prev = items[currentIndex - 1]
  const next = items[currentIndex + 1]

  return (
    <div className="api-docs__pager">
      <button
        className="api-docs__pager-btn"
        type="button"
        disabled={!prev}
        onClick={() => onStep(-1)}
      >
        <span>上一篇</span>
        <strong>{prev?.label ?? '无'}</strong>
      </button>
      <button
        className="api-docs__pager-btn api-docs__pager-btn--next"
        type="button"
        disabled={!next}
        onClick={() => onStep(1)}
      >
        <span>下一篇</span>
        <strong>{next?.label ?? '无'}</strong>
      </button>
    </div>
  )
}

// ---------- 各文档小节 ----------
function SectionWrap({
  id,
  title,
  desc,
  children,
}: {
  id: string
  title: string
  desc?: ReactNode
  children?: ReactNode
}) {
  return (
    <div className="api-docs__section">
      <h2 id={id} className="api-docs__h2">
        {title}
      </h2>
      {desc ? <Paragraph className="api-docs__summary" type="secondary">{desc}</Paragraph> : null}
      {children}
    </div>
  )
}

function OverviewSection() {
  return (
    <SectionWrap
      id="overview"
      title="概述"
      desc={
        <>
          面向商户系统调用的开放 API。凭证在「开放 API / API 密钥」创建，
          采用 <Text code>X-App-Key</Text> + HMAC-SHA256 签名鉴权。
          发货链路同时支持「返回签名链接」和「返回结构化数据」两种模式。
          <Text code>/api/open/v1</Text> 仍是上游回调链路，和本页的商户 API 分开。
        </>
      }
    >
      <Alert
        className="api-docs__alert"
        type="info"
        showIcon
        message="一句话上手"
        description={
          <ol style={{ margin: 0, paddingLeft: 20 }}>
            <li>在「开放 API / API 密钥」创建凭证，获得 <Text code>AppKey</Text> 与 <Text code>AppSecret</Text>。</li>
            <li>每次请求携带 <Text code>X-App-Key / X-Timestamp / X-Nonce / X-Sign</Text> 四个鉴权头。</li>
            <li>调用「商品列表」拿到可售 <Text code>sku</Text>，调用「下单」创建订单并扣款。</li>
            <li>拿到 <Text code>order_no</Text> 后，可选择返回发货链接，或者用结构化发货接口完成自建页面流程。</li>
          </ol>
        }
      />
      <Card className="api-docs__card" size="small" title="发货链路概览">
        <Table
          className="api-docs__table"
          size="small"
          pagination={false}
          rowKey="mode"
          dataSource={deliveryModes}
          columns={[
            { title: '模式', dataIndex: 'mode', width: 140 },
            { title: '适用场景', dataIndex: 'scene' },
            {
              title: '相关接口',
              dataIndex: 'interfaces',
              render: (v: string) => <pre className="api-docs__pre">{v}</pre>,
            },
          ]}
        />
      </Card>
      <Card className="api-docs__card" size="small" title="环境信息">
        <Descriptions size="small" column={1} bordered>
          <Descriptions.Item label="Base URL">
            <Text code>{baseUrl}</Text>
          </Descriptions.Item>
          <Descriptions.Item label="鉴权头">
            <Text code>X-App-Key</Text>、<Text code>X-Timestamp</Text>、<Text code>X-Nonce</Text>、<Text code>X-Sign</Text>
          </Descriptions.Item>
          <Descriptions.Item label="时间偏差">允许 ±300 秒</Descriptions.Item>
          <Descriptions.Item label="时间格式">
            所有时间字段统一返回 RFC3339 秒级北京时间，例如 <Text code>2026-07-30T17:53:58+08:00</Text>
          </Descriptions.Item>
          <Descriptions.Item label="请求体">POST 请求使用 <Text code>application/json</Text></Descriptions.Item>
        </Descriptions>
      </Card>
      <div className="api-docs__common">
        <CommonResponseDoc fields={commonResponseFields} errors={errorCodes} />
      </div>
    </SectionWrap>
  )
}

function AuthSection() {
  return (
    <SectionWrap id="auth" title="鉴权与签名">
      <Card className="api-docs__card" size="small" title="鉴权头（每次请求必带）">
        <Table
          className="api-docs__table"
          size="small"
          pagination={false}
          rowKey="header"
          dataSource={[
            { header: 'X-App-Key', required: true, desc: '商户分配的 AppKey' },
            { header: 'X-Timestamp', required: true, desc: '当前 Unix 秒时间戳' },
            { header: 'X-Nonce', required: true, desc: '随机串，8~96 字符，同一客户端有效期内不可重复' },
            { header: 'X-Sign', required: true, desc: 'HMAC-SHA256 签名，见下方算法' },
          ]}
          columns={[
            { title: 'Header', dataIndex: 'header', width: 160, render: (v) => <Text code>{v}</Text> },
            {
              title: '必填',
              dataIndex: 'required',
              width: 80,
              render: (v) => v ? <span className="api-docs__required">是</span> : <Text type="secondary">否</Text>,
            },
            { title: '说明', dataIndex: 'desc' },
          ]}
        />
      </Card>

      <Card className="api-docs__card" size="small" title="签名算法">
        <Descriptions size="small" column={1} bordered>
          <Descriptions.Item label="参与参数">
            <Text code>app_key</Text>、<Text code>body_sha256</Text>、<Text code>method</Text>、<Text code>nonce</Text>、<Text code>path</Text>、<Text code>timestamp</Text>
          </Descriptions.Item>
          <Descriptions.Item label="拼接方式">参数按 ASCII 字典序，用 <Text code>&</Text> 拼成 <Text code>k1=v1&k2=v2&...</Text></Descriptions.Item>
          <Descriptions.Item label="排序后顺序">
            <Text code>app_key, body_sha256, method, nonce, path, timestamp</Text>
          </Descriptions.Item>
          <Descriptions.Item label="method">大写，如 GET / POST</Descriptions.Item>
          <Descriptions.Item label="path">仅 URL.Path，不含域名和 query</Descriptions.Item>
          <Descriptions.Item label="body_sha256">
            <Text code>SHA256(原始 body 字节)</Text> 的十六进制小写；GET 用空 body 的摘要
          </Descriptions.Item>
          <Descriptions.Item label="value">原样拼接，不做 URL encode</Descriptions.Item>
          <Descriptions.Item label="X-Sign">
            <Text code>hex( HMAC-SHA256( app_secret, 签名串 ) )</Text>，小写十六进制
          </Descriptions.Item>
        </Descriptions>
      </Card>

      <Card className="api-docs__card" size="small" title="签名示例（GET，body 为空）">
        <pre className="api-docs__pre">{`app_key=ak_xxx&body_sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855&method=GET&nonce=a1b2c3d4e5f67890&path=/api/client/v1/products&timestamp=1721450000`}</pre>
      </Card>

      <Card className="api-docs__card" size="small" title="签名示例（POST，body 非空）">
        <pre className="api-docs__pre">{`# body = {"client_order_no":"shop-10001","sku":"suit_pink_sheep"}
app_key=ak_xxx&body_sha256=<实际请求 body 字节的 SHA256 十六进制>&method=POST&nonce=a1b2c3d4e5f67890&path=/api/client/v1/orders&timestamp=1721450000`}</pre>
      </Card>

      <Alert
        className="api-docs__alert"
        type="warning"
        showIcon
        message="签名注意事项"
        style={{ marginTop: 16 }}
        description={
          <ul style={{ margin: 0, paddingLeft: 20 }}>
            <li>POST 签名用的 <Text code>body</Text> 必须与实际发送的 body <b>字节级一致</b>，不要签名后再改空格或字段顺序。</li>
            <li>value <b>不要</b> URL encode，原样参与拼接。</li>
            <li>签名失败常见原因：secret 错、path 多了 query、body 与签名不一致、时间戳过期、nonce 重复、参数未按字典序拼接。</li>
            <li>下单接口还需携带 <Text code>Idempotency-Key</Text>，且必须与 <Text code>client_order_no</Text> 一致。</li>
          </ul>
        }
      />
    </SectionWrap>
  )
}

function EndpointSection({ spec }: { spec: EndpointSpec }) {
  return (
    <div className="api-docs__section">
      <div className="api-docs__endpoint-head">
        <h2 id={`ep-${spec.key}`} className="api-docs__h2">
          {spec.title}
        </h2>
        <div className="api-docs__endpoint-meta">
          <Tag color={spec.method === 'GET' ? 'blue' : 'geekblue'}>{spec.method}</Tag>
          <Text code>{spec.scope}</Text>
        </div>
      </div>
      <EndpointDoc spec={spec} />
    </div>
  )
}

function StatusSection() {
  return (
    <SectionWrap id="status" title="状态说明">
      <Card className="api-docs__card" size="small" title="履约状态 fulfillment_status 与 can_fulfill">
        <Table
          className="api-docs__table"
          size="small"
          pagination={false}
          rowKey="name"
          dataSource={orderStatusTable}
          columns={[
            { title: '状态', dataIndex: 'name', width: 140, render: (v) => <Text code>{v}</Text> },
            {
              title: 'can_fulfill',
              dataIndex: 'example',
              width: 160,
              render: (v: string) =>
                v === 'can_fulfill=true' ? <Tag color="green">true</Tag> : <Tag>false</Tag>,
            },
            { title: '说明', dataIndex: 'desc' },
          ]}
        />
      </Card>
      <Card className="api-docs__card" size="small" title="支付状态 payment_status">
        <Table
          className="api-docs__table"
          size="small"
          pagination={false}
          rowKey="name"
          dataSource={paymentStatusTable}
          columns={[
            { title: '状态', dataIndex: 'name', width: 140, render: (v) => <Text code>{v}</Text> },
            { title: '说明', dataIndex: 'desc' },
          ]}
        />
      </Card>
      <Alert
        className="api-docs__alert"
        type="info"
        showIcon
        message="推荐调用流程"
        style={{ marginTop: 16 }}
        description={
          <pre className="api-docs__pre api-docs__pre--plain">{`拿到 sku（商品列表）
    ↓
POST /orders 下单（幂等，Idempotency-Key = client_order_no）
    ↓
拿响应里的 order_no 进入发货平台链接
    ↓
发货平台查询订单并执行发货
    ↓
发货平台通过上游回调更新 fulfillment_status
    ↓
GET /orders/{order_no} 轮询，或接收 order.fulfillment.updated 回调`}</pre>
        }
      />
    </SectionWrap>
  )
}
