import { IconRefresh, IconSearch } from '@douyinfe/semi-icons';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { FormEvent, useDeferredValue, useEffect, useState } from 'react';
import { api } from '../../api/client';
import type { VehicleCoverageRow, VehicleLocationSourceEvidence, VehicleSourceDiagnostic } from '../../api/types';
import { InlineError } from '../shared/AsyncState';
import { LIVE_QUERY_POLICY, QUERY_MEMORY } from '../queryPolicy';

function statusLabel(status: string) {
  return { ok: '正常', warning: '关注', error: '异常' }[status] ?? status;
}

function fmt(value?: string) {
  if (!value) return '—';
  const parsed = new Date(value.replace(' ', 'T'));
  return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleString('zh-CN', { hour12: false });
}

function number(value?: number, digits = 1) {
  return value == null || !Number.isFinite(value) ? '—' : value.toLocaleString('zh-CN', { maximumFractionDigits: digits });
}

function SourcePolicyRow({ vin, source, diagnostic, editable, onSaved }: {
  vin: string;
  source: VehicleLocationSourceEvidence;
  diagnostic: VehicleSourceDiagnostic;
  editable: boolean;
  onSaved: (next: VehicleSourceDiagnostic) => void;
}) {
  const [enabled, setEnabled] = useState(source.enabled);
  const [priority, setPriority] = useState(source.priority);
  const [remark, setRemark] = useState('');
  useEffect(() => {
    setEnabled(source.enabled);
    setPriority(source.priority);
    setRemark('');
  }, [source.enabled, source.priority, source.sourceRef]);
  const save = useMutation({
    mutationFn: () => api.updateVehicleSourcePolicy(vin, {
      version: diagnostic.policy.version,
      sourceRef: source.sourceRef ?? '',
      enabled,
      priority,
      remark
    }),
    onSuccess: onSaved
  });
  const changed = enabled !== source.enabled || priority !== source.priority || remark.trim() !== '';
  return <tr className={source.recommended ? 'is-recommended' : ''}>
    <td><strong>{source.sourceLabel}</strong><span>{source.terminalLabel || source.sourceKind || '未维护终端'}</span></td>
    <td><b>{source.protocol}</b><span>{source.selectedWithinProtocol ? '协议内已选' : '协议内候选'}</span></td>
    <td><i className={source.online ? 'is-online' : 'is-offline'} />{source.online ? '在线' : '离线'}<span>{source.qualityStatus || '未知'}{source.qualityReason ? ` · ${source.qualityReason}` : ''}</span></td>
    <td><strong>{fmt(source.firstSeenAt)}</strong><span>最近 {fmt(source.receivedAt || source.eventTime)}</span></td>
    <td><strong>{source.reportIntervalSec == null ? '—' : `${source.reportIntervalSec}s`}</strong><span>{source.reportSampleCount ? `${source.reportSampleCount.toLocaleString('zh-CN')} 个里程样本` : '暂无累计样本'}</span></td>
    <td><strong>{source.longitude == null || source.latitude == null ? '—' : `${source.longitude.toFixed(6)}, ${source.latitude.toFixed(6)}`}</strong><span>{number(source.speedKmh)} km/h · {number(source.totalMileageKm)} km</span></td>
    <td className="v2-source-reason"><strong>{source.recommended ? '当前推荐' : source.selectedWithinProtocol ? '协议首选' : '备用来源'}</strong><span>{source.selectionReason || '等待选举说明'}</span></td>
    <td className="v2-source-policy-cell">
      <label><input type="checkbox" checked={enabled} disabled={!editable || save.isPending} onChange={(event) => setEnabled(event.target.checked)} />启用</label>
      <input aria-label={`${source.sourceLabel} 优先级`} type="number" min="1" max="1000" value={priority} disabled={!editable || save.isPending} onChange={(event) => setPriority(Number(event.target.value))} />
      <input aria-label={`${source.sourceLabel} 策略备注`} value={remark} maxLength={200} disabled={!editable || save.isPending} onChange={(event) => setRemark(event.target.value)} placeholder="调整原因" />
      <button type="button" disabled={!editable || !changed || save.isPending || !source.sourceRef || priority < 1 || priority > 1000} onClick={() => save.mutate()}>{save.isPending ? '保存中' : '保存策略'}</button>
      {save.isError ? <em role="alert">{save.error instanceof Error ? save.error.message : '保存失败'}</em> : null}
    </td>
  </tr>;
}

function SourceDiagnosticWorkspace() {
  const queryClient = useQueryClient();
  const [keyword, setKeyword] = useState('');
  const deferredKeyword = useDeferredValue(keyword.trim());
  const [selected, setSelected] = useState<VehicleCoverageRow>();
  const [candidateOffset, setCandidateOffset] = useState(0);
  useEffect(() => setCandidateOffset(0), [deferredKeyword]);
  const candidates = useQuery({
    queryKey: ['ops-source-candidates', deferredKeyword, candidateOffset],
    queryFn: ({ signal }) => api.vehicleCoverage(new URLSearchParams({ keyword: deferredKeyword, bindingStatus: 'bound', limit: '20', offset: String(candidateOffset) }), signal),
    enabled: deferredKeyword.length > 0,
    staleTime: 15_000,
    gcTime: QUERY_MEMORY.optionGcTime
  });
  const session = useQuery({ queryKey: ['ops-session'], queryFn: ({ signal }) => api.session(signal), staleTime: 60_000 });
  const diagnostic = useQuery({
    queryKey: ['ops-source-diagnostic', selected?.vin],
    queryFn: ({ signal }) => api.vehicleSourceDiagnostic(selected!.vin, signal),
    enabled: Boolean(selected?.vin),
    staleTime: 5_000,
    gcTime: QUERY_MEMORY.highVolumeGcTime
  });
  const choose = (vehicle: VehicleCoverageRow) => {
    setSelected(vehicle);
    setKeyword(vehicle.plate || vehicle.vin);
  };
  const submit = (event: FormEvent) => {
    event.preventDefault();
    const first = candidates.data?.items[0];
    if (first) choose(first);
  };
  const data = diagnostic.data;
  const editable = session.data?.role === 'admin';
  return <section className="v2-source-diagnostic">
    <header>
      <div><small>专业运维工作台</small><strong>单车多来源诊断</strong><span>按需查询，不加载全量 RAW；普通客户无权访问。</span></div>
      {selected ? <button type="button" onClick={() => diagnostic.refetch()} disabled={diagnostic.isFetching}><IconRefresh />刷新该车</button> : null}
    </header>
    <form className="v2-source-search" onSubmit={submit}>
      <label><IconSearch /><input aria-label="按车牌或 VIN 搜索诊断车辆" value={keyword} onChange={(event) => { setKeyword(event.target.value); setSelected(undefined); }} placeholder="输入车牌或 VIN，支持模糊搜索" /></label>
      <button type="submit" disabled={!candidates.data?.items.length}>诊断首个结果</button>
      {deferredKeyword && !selected ? <div className="v2-source-candidates">
        {candidates.isFetching ? <p>正在查询车辆…</p> : candidates.data?.items.map((vehicle) => <button type="button" key={vehicle.vin} onMouseDown={(event) => event.preventDefault()} onClick={() => choose(vehicle)}>
          <strong>{vehicle.plate || '未绑定车牌'}</strong><span>{vehicle.vin}</span><em>{vehicle.protocols.join(' / ') || '暂无来源'}</em>
        </button>)}
        {!candidates.isFetching && !candidates.data?.items.length ? <p>没有匹配的已绑定车辆</p> : null}
        {(candidates.data?.total ?? 0) > 20 ? <footer><span>第 {Math.floor(candidateOffset / 20) + 1} / {Math.ceil((candidates.data?.total ?? 0) / 20)} 页</span><div><button type="button" disabled={candidateOffset === 0} onClick={() => setCandidateOffset(Math.max(0, candidateOffset - 20))}>上一页</button><button type="button" disabled={candidateOffset + 20 >= (candidates.data?.total ?? 0)} onClick={() => setCandidateOffset(candidateOffset + 20)}>下一页</button></div></footer> : null}
      </div> : null}
    </form>
    {diagnostic.isError ? <InlineError message={diagnostic.error.message} onRetry={() => diagnostic.refetch()} /> : null}
    {!selected ? <div className="v2-source-empty"><strong>先选择一辆车</strong><span>将展示所有协议和同协议多终端、推荐原因、上报周期与可审计策略。</span></div> : null}
    {selected && diagnostic.isPending ? <div className="v2-source-empty"><strong>正在读取来源证据</strong><span>只查询当前车辆，不会扫描整车队。</span></div> : null}
    {data ? <>
      <div className="v2-source-summary">
        <article><small>车辆</small><strong>{data.evidence.plate || selected?.plate || '未绑定车牌'}</strong><span>{data.evidence.vin}</span></article>
        <article><small>当前推荐</small><strong>{data.evidence.recommendedLocationLabel || '暂无推荐'}</strong><span>{data.evidence.recommendedLocationProtocol || '—'}</span></article>
        <article><small>来源状态</small><strong>{data.evidence.locationSources.filter((item) => item.online).length} / {data.evidence.locationSources.length} 在线</strong><span>{data.evidence.locationConflict ? `位置冲突 ${number(data.evidence.conflictDistanceM)}m` : '未发现实时位置冲突'}</span></article>
        <article><small>策略版本</small><strong>v{data.policy.version}</strong><span>{data.policy.updatedAt ? `${data.policy.updatedBy} · ${fmt(data.policy.updatedAt)}` : '尚无人工调整'}</span></article>
      </div>
      <div className="v2-source-recommendation"><strong>推荐说明</strong><p>{data.recommendationReason}</p><span>{data.refreshHint}</span></div>
      {!editable ? <p className="v2-source-readonly">当前账号可查看诊断证据；只有管理员可以调整启停和优先级。</p> : null}
      <div className="v2-source-table-wrap"><table className="v2-source-table"><thead><tr><th>来源 / 终端</th><th>协议</th><th>在线 / 质量</th><th>首次 / 最近上报</th><th>上报周期</th><th>位置 / 里程</th><th>选举结论</th><th>运维策略</th></tr></thead><tbody>
        {data.evidence.locationSources.map((source) => <SourcePolicyRow key={source.sourceRef || `${source.protocol}-${source.sourceLabel}-${source.terminalLabel}`} vin={data.evidence.vin} source={source} diagnostic={data} editable={editable && Boolean(source.sourceRef)} onSaved={(next) => queryClient.setQueryData(['ops-source-diagnostic', data.evidence.vin], next)} />)}
      </tbody></table></div>
      <section className="v2-source-audit"><header><strong>最近策略审计</strong><span>仅记录实际变更，原始 source_key 不对前端暴露</span></header>
        {data.policy.audit.length ? <ol>{data.policy.audit.map((item) => <li key={`${item.version}-${item.sourceRef}`}><b>v{item.version}</b><span>{item.summary}</span><em>{item.actor} · {fmt(item.changedAt)}</em></li>)}</ol> : <p>该车辆尚无人工来源策略变更。</p>}
      </section>
    </> : null}
  </section>;
}

export default function OperationsPage() {
  const health = useQuery({ queryKey: ['ops-health-v2'], queryFn: ({ signal }) => api.opsHealth(signal), refetchInterval: 15_000, staleTime: 8_000, gcTime: QUERY_MEMORY.summaryGcTime, ...LIVE_QUERY_POLICY });
  const readiness = useQuery({ queryKey: ['ops-source-readiness-v2'], queryFn: ({ signal }) => api.sourceReadiness(signal), refetchInterval: 30_000, staleTime: 15_000, gcTime: QUERY_MEMORY.summaryGcTime, ...LIVE_QUERY_POLICY });
  const data = health.data; const sources = readiness.data;
  const refresh = () => Promise.all([health.refetch(), readiness.refetch()]);
  return <div className="v2-ops-page">
    <header className="v2-ops-heading"><div><h2>运维质量</h2><p>先定位单车多来源问题，再核对服务和协议全局健康；所有结论来自服务端证据。</p></div><button onClick={refresh} disabled={health.isFetching || readiness.isFetching}><IconRefresh />刷新全局证据</button></header>
    <SourceDiagnosticWorkspace />
    {health.isError ? <InlineError message={health.error.message} onRetry={refresh} /> : null}
    {readiness.isError ? <InlineError message={readiness.error instanceof Error ? readiness.error.message : '协议来源就绪度读取失败'} onRetry={() => readiness.refetch()} /> : null}
    <section className="v2-ops-kpis">
      <article><small>运行版本</small><strong>{data?.runtime.platformRelease || '未注入'}</strong><span className={data?.runtime.dataMode === 'production' ? 'is-ok' : 'is-error'}>{data?.runtime.dataMode || 'unknown'}</span></article>
      <article><small>活跃连接</small><strong>{data?.activeConnections?.toLocaleString('zh-CN') ?? '—'}</strong><span>capacity-check</span></article>
      <article><small>Kafka Lag</small><strong>{data?.kafkaLag?.toLocaleString('zh-CN') ?? '—'}</strong><span className={data?.kafkaLag === 0 ? 'is-ok' : 'is-warning'}>{data?.kafkaLag === 0 ? '已回零' : '需检查'}</span></article>
      <article><small>Redis 在线 Key</small><strong>{data?.redisOnlineKeys?.toLocaleString('zh-CN') ?? '—'}</strong><span>实时探针</span></article>
      <article><small>服务身份 / 在线</small><strong>{sources ? `${sources.totalVehicles} / ${sources.onlineVehicles}` : '—'}</strong><span>{sources ? `已绑定 ${sources.boundVehicles} · 待绑定 ${sources.identityRequiredVehicles}` : '档案与快照并集'}</span></article>
    </section>
    <div className="v2-ops-grid"><section className="v2-ops-links"><header><strong>数据链路</strong><span>15 秒自动刷新</span></header><div>{data?.linkHealth.map((item) => <article key={item.name}><i className={`is-${item.status}`} /><div><strong>{item.name}</strong><p>{item.detail || '无补充信息'}</p></div><span className={`is-${item.status}`}>{statusLabel(item.status)}</span></article>)}</div></section>
      <section className="v2-ops-runtime"><header><strong>运行时安全</strong></header><dl><div><dt>生产数据模式</dt><dd>{data?.runtime.dataMode === 'production' ? '已启用' : '未启用'}</dd></div><div><dt>MySQL 写探针</dt><dd className={data?.mysqlWritable ? 'is-ok' : 'is-error'}>{data?.mysqlWritable ? '正常' : '异常'}</dd></div><div><dt>TDengine 写探针</dt><dd className={data?.tdengineWritable ? 'is-ok' : 'is-error'}>{data?.tdengineWritable ? '正常' : '异常'}</dd></div><div><dt>请求超时</dt><dd>{data?.runtime.requestTimeoutMs ?? '—'} ms</dd></div><div><dt>高德安全代理</dt><dd className={data?.runtime.amapSecurityProxyEnabled && !data?.runtime.amapSecurityCodeExposed ? 'is-ok' : 'is-warning'}>{data?.runtime.amapSecurityProxyEnabled ? '服务端代理' : '未启用'}</dd></div></dl>{data?.capacityFindings?.length ? <div className="v2-ops-findings">{data.capacityFindings.map((item) => <p key={item}>{item}</p>)}</div> : <p className="v2-ops-clear">容量检查无待处理项</p>}</section></div>
    <section className="v2-ops-sources"><header><strong>协议来源就绪度</strong><span>验收口径与处置建议</span></header><div>{sources?.sources.map((source) => <article key={source.protocol}><div><i className={`is-${source.severity}`} /><strong>{source.protocol}</strong><span>{source.role}</span></div><b>{source.online} / {source.total} 在线</b><p>{source.evidence}</p><p>{source.action}</p><em>{source.acceptance}</em></article>)}</div></section>
  </div>;
}
