/**
 * ScoreGauge — 风险总分仪表盘（task 19.3，Req 20.1 / 20.6）。
 *
 * 同时呈现 Risk_Score 数值与其对应的 Risk_Grade 文本标签（Req 20.6）：
 *   - Risk_Grade 由本地 `classifyGrade(score)` 派生，逐字镜像领域层分级规则，
 *     因此仪表盘展示的等级恒等于该分值的分类结果（task 19.9 / Property 71）。
 *   - 等级配色经 `riskGradeColorToken(grade)` 取得稳定 Color_Token，最终取值由
 *     ThemeProvider 解析为 CSS 变量，不在图表层硬编码具体颜色（Req 19.6）。
 *
 * 通过通用 `ChartContainer` 取得一致的框架（标题 / 三态 / 标签），仪表盘图形以
 * 确定性 SVG 半圆弧表达，分值与等级以可见文本节点呈现（便于无障碍与测试）。
 * loading / empty 三态由容器统一处理（Req 20.4 / 20.5）。
 */

import type { CSSProperties } from 'react';
import {
  colorTokenToCssVarName,
  riskGradeColorToken,
  spacing,
  typography,
} from '../design-system/index.js';
import type { ColorToken } from '../design-system/index.js';
import { ChartContainer } from './ChartContainer.js';
import type { ChartSpec, Label, Series } from './chart-types.js';
import { classifyGrade } from './riskGrade.js';

/* ------------------------------------------------------------------ *
 * 局部样式原语（取自 Design Tokens，颜色经 CSS 变量引用，与 ChartContainer 一致）
 * ------------------------------------------------------------------ */

/** 将 Color_Token 映射为 CSS 变量引用。 */
function colorVar(token: ColorToken): string {
  return `var(${colorTokenToCssVarName(token)})`;
}

/** 取间距标度第 `step` 档（px）；越界回退为 0。 */
function space(step: number): number {
  return spacing[step] ?? 0;
}

const FONT_FAMILY =
  "'Inter', 'PingFang SC', 'Microsoft YaHei', system-ui, -apple-system, sans-serif";

/** 取具名排版层级的字体样式（找不到则回退到 body 近似值）。 */
function typographyStyle(name: string): CSSProperties {
  const level = typography.find((t) => t.name === name);
  if (level === undefined) {
    return { fontSize: '14px', lineHeight: '22px' };
  }
  return { fontSize: `${level.fontSize}px`, lineHeight: `${level.lineHeight}px` };
}

/* ------------------------------------------------------------------ *
 * Risk_Score 取值域
 * ------------------------------------------------------------------ */

/** Risk_Score 下界（Req：0 至 100）。 */
const SCORE_MIN = 0;
/** Risk_Score 上界（Req：0 至 100）。 */
const SCORE_MAX = 100;

/** 将任意输入夹取到 [0, 100] 取值域内，保证弧形渲染稳健。 */
function clampScore(score: number): number {
  if (Number.isNaN(score)) {
    return SCORE_MIN;
  }
  if (score < SCORE_MIN) {
    return SCORE_MIN;
  }
  if (score > SCORE_MAX) {
    return SCORE_MAX;
  }
  return score;
}

/* ------------------------------------------------------------------ *
 * 半圆弧几何（确定性，无外部依赖）
 * ------------------------------------------------------------------ */

const GAUGE_WIDTH = 220;
const GAUGE_HEIGHT = 120;
const GAUGE_CX = GAUGE_WIDTH / 2;
const GAUGE_CY = GAUGE_HEIGHT - 10;
const GAUGE_RADIUS = 90;
const GAUGE_STROKE = 16;

/** 极坐标 → 笛卡尔坐标（角度以度计，0°=正左，180°=正右，沿上半圆）。 */
function polar(angleDeg: number): { readonly x: number; readonly y: number } {
  const rad = (Math.PI * (180 - angleDeg)) / 180;
  return {
    x: GAUGE_CX + GAUGE_RADIUS * Math.cos(rad),
    y: GAUGE_CY - GAUGE_RADIUS * Math.sin(rad),
  };
}

/** 构造从 `startDeg` 到 `endDeg` 的半圆弧 path 数据（上半圆，0..180°）。 */
function arcPath(startDeg: number, endDeg: number): string {
  const start = polar(startDeg);
  const end = polar(endDeg);
  const largeArc = endDeg - startDeg > 180 ? 1 : 0;
  return `M ${start.x} ${start.y} A ${GAUGE_RADIUS} ${GAUGE_RADIUS} 0 ${largeArc} 1 ${end.x} ${end.y}`;
}

/* ------------------------------------------------------------------ *
 * 仪表盘图形（SVG）
 * ------------------------------------------------------------------ */

function GaugeArc({
  score,
  gradeColorToken,
}: {
  readonly score: number;
  readonly gradeColorToken: ColorToken;
}): JSX.Element {
  // 分值在 [0,100] 线性映射到半圆 [0°,180°]。
  const sweep = (score - SCORE_MIN) / (SCORE_MAX - SCORE_MIN) * 180;
  return (
    <svg
      width={GAUGE_WIDTH}
      height={GAUGE_HEIGHT}
      viewBox={`0 0 ${GAUGE_WIDTH} ${GAUGE_HEIGHT}`}
      role="img"
      aria-hidden={true}
      focusable={false}
      data-gauge-arc="true"
    >
      {/* 轨道：完整半圆底色。 */}
      <path
        d={arcPath(0, 180)}
        fill="none"
        stroke={colorVar('color.border.default')}
        strokeWidth={GAUGE_STROKE}
        strokeLinecap="round"
      />
      {/* 进度弧：长度对应分值，配色取自 Risk_Grade 的 Color_Token。 */}
      {sweep > 0 ? (
        <path
          d={arcPath(0, sweep)}
          fill="none"
          stroke={colorVar(gradeColorToken)}
          strokeWidth={GAUGE_STROKE}
          strokeLinecap="round"
          data-gauge-progress="true"
        />
      ) : null}
    </svg>
  );
}

/* ------------------------------------------------------------------ *
 * ScoreGauge
 * ------------------------------------------------------------------ */

/** `ScoreGauge` 组件属性。 */
export interface ScoreGaugeProps {
  /** Risk_Score 数值（期望 0 至 100；越界将被夹取以稳健渲染）。 */
  readonly score: number;
  /** 数据是否正在计算/请求中（true → 由容器呈现 Loading_State，Req 20.5）。 */
  readonly loading?: boolean;
  /** 可选标题，缺省为「风险总分」。 */
  readonly title?: string;
}

/**
 * 风险总分仪表盘：同时呈现 Risk_Score 数值与其对应 Risk_Grade（Req 20.6）。
 *
 * 等级由 `classifyGrade(score)` 派生（与领域分级规则一致），等级配色取自
 * `riskGradeColorToken(grade)`。分值与等级文本均以可见 DOM 文本节点呈现，
 * 并附 `data-risk-score` / `data-risk-grade` 便于测试与无障碍读取。
 */
export function ScoreGauge({
  score,
  loading,
  title,
}: ScoreGaugeProps): JSX.Element {
  const safeScore = clampScore(score);
  const grade = classifyGrade(safeScore);
  const gradeColorToken = riskGradeColorToken(grade);

  // 单一系列的仪表盘：以一个数据点承载分值，使容器进入就绪态并渲染图形。
  const series: Series[] = loading === true
    ? []
    : [
        {
          id: 'risk-score',
          label: '风险总分',
          encoding: {
            colorToken: gradeColorToken,
            textLabel: `风险总分（${grade}）`,
            pattern: 'solid',
          },
          points: [{ label: `Risk_Score：${safeScore}`, value: safeScore }],
        },
      ];

  const labels: Label[] = [
    { kind: 'axis', text: 'Risk_Score（0–100）' },
    { kind: 'partition', text: `Risk_Grade：${grade}` },
  ];

  const spec: ChartSpec = {
    type: 'ScoreGauge',
    status: loading === true ? 'loading' : 'ready',
    series,
    labels,
    title: title ?? '风险总分',
    emptyMessage: '暂无可展示的风险总分',
    loadingMessage: '正在计算风险总分…',
  };

  return (
    <ChartContainer spec={spec}>
      <div
        data-score-gauge="true"
        style={{
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          gap: `${space(2)}px`,
          fontFamily: FONT_FAMILY,
        }}
      >
        <GaugeArc score={safeScore} gradeColorToken={gradeColorToken} />
        <div
          style={{
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            gap: `${space(1)}px`,
          }}
        >
          {/* Risk_Score 数值（Req 20.6）。 */}
          <span
            data-risk-score={safeScore}
            aria-label={`风险总分 ${safeScore}`}
            style={{
              ...typographyStyle('display'),
              fontWeight: 700,
              color: colorVar(gradeColorToken),
            }}
          >
            {safeScore}
          </span>
          {/* Risk_Grade 文本标签（Req 20.6）。 */}
          <span
            data-risk-grade={grade}
            aria-label={`风险分级 ${grade}`}
            style={{
              ...typographyStyle('title'),
              fontWeight: 700,
              color: colorVar(gradeColorToken),
            }}
          >
            {grade}
          </span>
        </div>
      </div>
    </ChartContainer>
  );
}
