import React from 'react';

type KpiCard = {
  title: string;
  value: any;
  subtitle: string;
  trend: string;
  trendDirection: string;
  tone: string;
  prefix: string;
  suffix: string;
  format: string;
  decimals?: number;
  icon: string;
  targetLabel: string;
  targetValue: string;
};

export class CodeInApplication implements CodeInComp {
  description() {
    return "在易企办仪表盘展示可配置的关键指标卡片。";
  }

  requiredFields(params?: CodeInParams) {
    return [];
  }

  inputParameters(): InputParameter[] {
    return [
      {
        id: 'cardsConfig',
        name: "卡片数据",
        type: 'variable',
        desc: "指标卡片配置，支持 JSON 数组、包含 cards、items 或 data 的对象、变量或表达式结果。",
      },
      {
        id: 'titleText',
        name: "标题",
        type: 'string',
        desc: "显示在卡片组上方的可选标题。",
      },
      {
        id: 'subtitleText',
        name: "副标题",
        type: 'string',
        desc: "标题下方的辅助说明。",
      },
      {
        id: 'layoutMode',
        name: "卡片布局",
        type: 'string',
        desc: "支持自动布局或固定一至四列，默认 auto。",
      },
      {
        id: 'cardTone',
        name: "默认卡片色调",
        type: 'string',
        desc: "卡片未单独指定色调时使用。可用 blue、green、amber、red、violet、slate。",
      },
      {
        id: 'titleField',
        name: "标题字段",
        type: 'variable',
        desc: "用于读取指标名称的可选字段，例如 MetricName。",
      },
      {
        id: 'valueField',
        name: "数值字段",
        type: 'variable',
        desc: "用于读取指标值的可选字段，例如 MetricValue。",
      },
      {
        id: 'subtitleField',
        name: "副标题字段",
        type: 'variable',
        desc: "用于读取卡片辅助说明的字段。",
      },
      {
        id: 'trendField',
        name: "趋势字段",
        type: 'variable',
        desc: "用于读取变化值或趋势说明的字段。",
      },
      {
        id: 'trendDirectionField',
        name: "趋势方向字段",
        type: 'variable',
        desc: "用于读取趋势方向，值可为 up、down 或 neutral。",
      },
      {
        id: 'toneField',
        name: "色调字段",
        type: 'variable',
        desc: "用于读取卡片色调，值可为 blue、green、amber、red、violet 或 slate。",
      },
      {
        id: 'formatField',
        name: "格式字段",
        type: 'variable',
        desc: "用于读取数值格式，可用 auto、number、percent 或 text。",
      },
      {
        id: 'prefixField',
        name: "前缀字段",
        type: 'variable',
        desc: "用于读取数值前方的文字。",
      },
      {
        id: 'suffixField',
        name: "后缀字段",
        type: 'variable',
        desc: "用于读取数值后方的单位或文字。",
      },
      {
        id: 'iconField',
        name: "角标字段",
        type: 'variable',
        desc: "用于读取卡片角标文字。",
      },
      {
        id: 'targetLabelField',
        name: "目标名称字段",
        type: 'variable',
        desc: "用于读取目标的名称。",
      },
      {
        id: 'targetValueField',
        name: "目标值字段",
        type: 'variable',
        desc: "用于读取目标值。",
      },
      {
        id: 'decimalsField',
        name: "小数位字段",
        type: 'variable',
        desc: "用于读取每张卡片的小数位数。",
      },
      {
        id: 'showTrend',
        name: "显示趋势",
        type: 'variable',
        desc: "控制变化值或趋势说明显示，支持布尔值或表达式。",
      },
      {
        id: 'showIcon',
        name: "显示角标",
        type: 'variable',
        desc: "控制卡片角标显示，支持布尔值或表达式。",
      },
      {
        id: 'compactMode',
        name: "紧凑布局",
        type: 'variable',
        desc: "启用后缩小间距，支持布尔值或表达式。",
      },
      {
        id: 'emptyStateText',
        name: "空状态提示",
        type: 'string',
        desc: "缺少卡片配置时的提示。",
      },
      {
        id: 'numberLocale',
        name: "数字地区格式",
        type: 'string',
        desc: "数字格式的语言区域，默认 zh-CN。",
      },
      {
        id: 'decimalPlaces',
        name: "默认小数位数",
        type: 'string',
        desc: "所有数值卡片的默认小数位数，默认为 0。",
      },
      {
        id: 'minCardWidth',
        name: "最小卡片宽度",
        type: 'string',
        desc: "自动布局时的最小卡片宽度，默认为 220 像素。",
      },
      {
        id: 'valueSize',
        name: "数值字号",
        type: 'string',
        desc: "可设为 small、medium 或 large，默认为 medium。",
      },
    ];
  }

  render(context: CodeInContext, fieldsValues: any, readonly: boolean) {
    return (
      <KpiCardSet
        context={context}
        fieldsValues={fieldsValues}
        readonly={readonly}
        params={(context && context.params) || {}}
      />
    );
  }
}

class KpiCardSet extends React.Component<any, any> {
  getConfig() {
    const params = this.props.params || {};
    const defaultTone = this.normalizeTone(this.normalizeToString(params.cardTone) || 'blue');
    const fieldMap = {
      title: this.normalizeToString(params.titleField),
      value: this.normalizeToString(params.valueField),
      subtitle: this.normalizeToString(params.subtitleField),
      trend: this.normalizeToString(params.trendField),
      trendDirection: this.normalizeToString(params.trendDirectionField),
      tone: this.normalizeToString(params.toneField),
      format: this.normalizeToString(params.formatField),
      prefix: this.normalizeToString(params.prefixField),
      suffix: this.normalizeToString(params.suffixField),
      icon: this.normalizeToString(params.iconField),
      targetLabel: this.normalizeToString(params.targetLabelField),
      targetValue: this.normalizeToString(params.targetValueField),
      decimals: this.normalizeToString(params.decimalsField),
    };
    return {
      cards: this.parseCardsConfig(params.cardsConfig, defaultTone, fieldMap),
      titleText: this.normalizeToString(params.titleText),
      subtitleText: this.normalizeToString(params.subtitleText),
      layoutMode: this.normalizeLayoutMode(this.normalizeToString(params.layoutMode)),
      cardTone: defaultTone,
      fieldMap: fieldMap,
      showTrend: this.normalizeToBoolean(params.showTrend, true),
      showIcon: this.normalizeToBoolean(params.showIcon, true),
      compactMode: this.normalizeToBoolean(params.compactMode, false),
      emptyStateText: this.normalizeToString(params.emptyStateText) || "暂无已配置的指标卡片",
      numberLocale: this.normalizeToString(params.numberLocale),
      decimalPlaces: this.normalizeToNumber(params.decimalPlaces, 0),
      minCardWidth: Math.max(160, this.normalizeToNumber(params.minCardWidth, 220)),
      valueSize: this.normalizeValueSize(this.normalizeToString(params.valueSize)),
    };
  }

  parseCardsConfig(rawValue: any, defaultTone: string, fieldMap: any): KpiCard[] {
    const resolved = this.resolveParameterValue(rawValue);
    const parsed = this.safeParseJson(resolved);
    const source = parsed !== null && parsed !== undefined ? parsed : resolved;
    const cardSource = this.extractCardArray(source);
    return cardSource
      .map((item: any, index: number) => this.normalizeCard(item, index, defaultTone, fieldMap))
      .filter((card: KpiCard | null) => Boolean(card)) as KpiCard[];
  }

  extractCardArray(value: any): any[] {
    if (Array.isArray(value)) {
      return value;
    }
    if (value && typeof value === 'object') {
      const candidates = [
        value.cards,
        value.Cards,
        value.items,
        value.Items,
        value.data,
        value.Data,
        value.list,
        value.List,
        value.values,
        value.Values,
      ];
      for (let index = 0; index < candidates.length; index += 1) {
        if (Array.isArray(candidates[index])) {
          return candidates[index];
        }
      }
      if (value.title !== undefined || value.label !== undefined || value.value !== undefined) {
        return [value];
      }
    }
    const text = this.normalizeToString(value);
    if (!text) {
      return [];
    }
    /* 也支持逐行配置，格式为「标题 | 值 | 副标题 | 趋势 | 色调」。推荐优先使用 JSON。 */
    return text
      .split(/\r?\n/)
      .map((line) => line.trim())
      .filter(Boolean)
      .map((line) => line.split('|').map((part) => part.trim()));
  }

  normalizeCard(item: any, index: number, defaultTone: string, fieldMap: any): KpiCard | null {
    if (Array.isArray(item)) {
      const title = this.normalizeToString(item[0]);
      const value = item.length > 1 ? item[1] : '';
      if (!title && this.normalizeToString(value) === '') {
        return null;
      }
      return {
        title: title || "指标 " + (index + 1),
        value: value,
        subtitle: this.normalizeToString(item[2]),
        trend: this.normalizeToString(item[3]),
        trendDirection: this.inferTrendDirection(item[3]),
        tone: this.normalizeTone(this.normalizeToString(item[4]) || defaultTone),
        prefix: '',
        suffix: '',
        format: 'auto',
        icon: '',
        targetLabel: '',
        targetValue: '',
      };
    }

    if (item && typeof item === 'object') {
      const title = this.normalizeToString(
        this.firstDefined([
          this.readMappedField(item, fieldMap.title),
          item.title,
          item.label,
          item.name,
          item.heading,
        ])
      );
      const value = this.firstDefined([
        this.readMappedField(item, fieldMap.value),
        item.value,
        item.count,
        item.number,
        item.total,
        item.amount,
      ]);
      if (!title && this.normalizeToString(value) === '') {
        return null;
      }
      const trend = this.normalizeToString(
        this.firstDefined([
          this.readMappedField(item, fieldMap.trend),
          item.trend,
          item.trendValue,
          item.change,
          item.delta,
        ])
      );
      const trendDirection = this.normalizeToString(
        this.firstDefined([
          this.readMappedField(item, fieldMap.trendDirection),
          item.trendDirection,
          item.direction,
          item.status,
        ])
      ) || this.inferTrendDirection(trend);
      const decimalsValue = this.firstDefined([
        this.readMappedField(item, fieldMap.decimals),
        item.decimals,
      ]);
      return {
        title: title || "指标 " + (index + 1),
        value: value,
        subtitle: this.normalizeToString(
          this.firstDefined([
            this.readMappedField(item, fieldMap.subtitle),
            item.subtitle,
            item.helper,
            item.description,
            item.caption,
          ])
        ),
        trend: trend,
        trendDirection: this.normalizeTrendDirection(trendDirection),
        tone: this.normalizeTone(this.normalizeToString(this.firstDefined([
          this.readMappedField(item, fieldMap.tone),
          item.tone,
          item.color,
          item.accent,
        ])) || defaultTone),
        prefix: this.normalizeToString(this.firstDefined([this.readMappedField(item, fieldMap.prefix), item.prefix])),
        suffix: this.normalizeToString(this.firstDefined([this.readMappedField(item, fieldMap.suffix), item.suffix])),
        format: this.normalizeFormat(this.normalizeToString(this.firstDefined([this.readMappedField(item, fieldMap.format), item.format]))),
        decimals: decimalsValue !== undefined ? this.normalizeToNumber(decimalsValue, 0) : undefined,
        icon: this.normalizeToString(this.firstDefined([this.readMappedField(item, fieldMap.icon), item.icon, item.initials])),
        targetLabel: this.normalizeToString(this.firstDefined([this.readMappedField(item, fieldMap.targetLabel), item.targetLabel, item.targetName])),
        targetValue: this.normalizeToString(this.firstDefined([this.readMappedField(item, fieldMap.targetValue), item.targetValue, item.target])),
      };
    }

    const primitiveValue = this.normalizeToString(item);
    if (!primitiveValue) {
      return null;
    }
    return {
      title: "指标 " + (index + 1),
      value: primitiveValue,
      subtitle: '',
      trend: '',
      trendDirection: 'neutral',
      tone: defaultTone,
      prefix: '',
      suffix: '',
      format: 'auto',
      icon: '',
      targetLabel: '',
      targetValue: '',
    };
  }

  firstDefined(values: any[]) {
    for (let index = 0; index < values.length; index += 1) {
      if (values[index] !== undefined && values[index] !== null) {
        return values[index];
      }
    }
    return undefined;
  }

  readMappedField(item: any, fieldName: string): any {
    const fieldPath = this.normalizeToString(fieldName);
    if (!item || !fieldPath) {
      return undefined;
    }
    const directValue = this.readObjectValueByKey(item, fieldPath);
    if (directValue !== undefined) {
      return directValue;
    }
    if (fieldPath.indexOf('.') !== -1) {
      const parts = fieldPath.split('.');
      let current = item;
      for (let index = 0; index < parts.length; index += 1) {
        current = this.readObjectValueByKey(current, parts[index]);
        if (current === undefined || current === null) {
          return undefined;
        }
      }
      return current;
    }
    return undefined;
  }

  readObjectValueByKey(source: any, wantedKey: string): any {
    if (!source || typeof source !== 'object' || Array.isArray(source) || !wantedKey) {
      return undefined;
    }
    if (source[wantedKey] !== undefined) {
      return source[wantedKey];
    }
    const normalizedWanted = wantedKey.toLowerCase();
    const keys = Object.keys(source);
    for (let index = 0; index < keys.length; index += 1) {
      if (keys[index].toLowerCase() === normalizedWanted) {
        return source[keys[index]];
      }
    }
    return undefined;
  }

  resolveParameterValue(value: any): any {
    if (value === undefined || value === null) {
      return '';
    }
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || Array.isArray(value)) {
      return value;
    }
    if (typeof value === 'object') {
      const preferred =
        value.value !== undefined ? value.value :
        value.Value !== undefined ? value.Value :
        value.data !== undefined ? value.data :
        value.Data !== undefined ? value.Data :
        value.items !== undefined ? value.items :
        value.Items !== undefined ? value.Items :
        value.cards !== undefined ? value.cards :
        value.Cards !== undefined ? value.Cards :
        value.result !== undefined ? value.result :
        value.Result !== undefined ? value.Result :
        value.key !== undefined ? value.key :
        value.Key !== undefined ? value.Key :
        value.label !== undefined ? value.label :
        value.Label !== undefined ? value.Label : undefined;
      if (preferred !== undefined) {
        return this.resolveParameterValue(preferred);
      }
    }
    return value;
  }

  safeParseJson(value: any): any {
    if (Array.isArray(value) || (value && typeof value === 'object')) {
      return value;
    }
    const text = this.normalizeToString(value);
    if (!text) {
      return null;
    }
    try {
      return JSON.parse(text);
    } catch (error) {
      return null;
    }
  }

  normalizeToString(value: any): string {
    if (value === undefined || value === null) {
      return '';
    }
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
      return String(value).trim();
    }
    if (Array.isArray(value)) {
      return value.map((item) => this.normalizeToString(item)).filter(Boolean).join(', ');
    }
    if (typeof value === 'object') {
      const resolved =
        value.value !== undefined ? value.value :
        value.Value !== undefined ? value.Value :
        value.text !== undefined ? value.text :
        value.Text !== undefined ? value.Text :
        value.display !== undefined ? value.display :
        value.Display !== undefined ? value.Display :
        value.label !== undefined ? value.label :
        value.Label !== undefined ? value.Label :
        value.name !== undefined ? value.name :
        value.Name !== undefined ? value.Name :
        value.title !== undefined ? value.title :
        value.Title !== undefined ? value.Title :
        value.key !== undefined ? value.key :
        value.Key !== undefined ? value.Key : undefined;
      if (resolved !== undefined) {
        return this.normalizeToString(resolved);
      }
      try {
        return JSON.stringify(value);
      } catch (error) {
        return '';
      }
    }
    return String(value).trim();
  }

  normalizeToBoolean(value: any, fallback: boolean): boolean {
    if (value === true || value === false) {
      return value;
    }
    if (Array.isArray(value) && value.length > 0) {
      return this.normalizeToBoolean(value[0], fallback);
    }
    if (value && typeof value === 'object') {
      const objectValue =
        value.value !== undefined ? value.value :
        value.Value !== undefined ? value.Value :
        value.checked !== undefined ? value.checked :
        value.Checked !== undefined ? value.Checked :
        value.selected !== undefined ? value.selected :
        value.Selected !== undefined ? value.Selected :
        value.key !== undefined ? value.key :
        value.Key !== undefined ? value.Key :
        value.label !== undefined ? value.label :
        value.Label !== undefined ? value.Label : undefined;
      if (objectValue !== undefined) {
        return this.normalizeToBoolean(objectValue, fallback);
      }
    }
    const text = this.normalizeToString(value).toLowerCase();
    if (['true', '1', 'yes', 'y', 'on', 'show', 'enabled'].indexOf(text) !== -1) {
      return true;
    }
    if (['false', '0', 'no', 'n', 'off', 'hide', 'disabled'].indexOf(text) !== -1) {
      return false;
    }
    return fallback;
  }

  normalizeToNumber(value: any, fallback: number): number {
    if (typeof value === 'number' && isFinite(value)) {
      return value;
    }
    const text = this.normalizeToString(value).replace(/,/g, '').replace(/%/g, '');
    if (!text) {
      return fallback;
    }
    const parsed = parseFloat(text);
    return isFinite(parsed) ? parsed : fallback;
  }

  formatValue(card: KpiCard, config: any): string {
    const rawText = this.normalizeToString(card.value);
    const format = this.normalizeFormat(card.format);
    if (format === 'text') {
      return card.prefix + rawText + card.suffix;
    }
    const numericValue = this.toNumberOrNull(card.value);
    if (numericValue === null || format === 'auto' && !this.looksNumeric(rawText)) {
      return card.prefix + rawText + card.suffix;
    }
    const decimals = card.decimals !== undefined ? card.decimals : config.decimalPlaces;
    const normalizedDecimals = Math.max(0, Math.min(6, decimals));
    let formatted = '';
    try {
      formatted = numericValue.toLocaleString(config.numberLocale || "zh-CN", {
        minimumFractionDigits: normalizedDecimals,
        maximumFractionDigits: normalizedDecimals,
      });
    } catch (error) {
      formatted = String(numericValue);
    }
    if (format === 'percent') {
      return card.prefix + formatted + '%' + card.suffix;
    }
    return card.prefix + formatted + card.suffix;
  }

  toNumberOrNull(value: any): number | null {
    if (typeof value === 'number' && isFinite(value)) {
      return value;
    }
    const text = this.normalizeToString(value).replace(/,/g, '').replace(/%/g, '');
    if (!text) {
      return null;
    }
    const parsed = parseFloat(text);
    return isFinite(parsed) ? parsed : null;
  }

  looksNumeric(value: string) {
    return /^-?\d+(\.\d+)?%?$/.test((value || '').replace(/,/g, '').trim());
  }

  normalizeLayoutMode(value: string) {
    const normalized = (value || '').toLowerCase();
    if (['one', 'two', 'three', 'four', 'auto'].indexOf(normalized) !== -1) {
      return normalized;
    }
    if (['1', '2', '3', '4'].indexOf(normalized) !== -1) {
      return ['one', 'two', 'three', 'four'][parseInt(normalized, 10) - 1];
    }
    return 'auto';
  }

  normalizeTone(value: string) {
    const normalized = (value || '').toLowerCase();
    if (['blue', 'green', 'amber', 'red', 'violet', 'slate'].indexOf(normalized) !== -1) {
      return normalized;
    }
    return 'blue';
  }

  normalizeFormat(value: string) {
    const normalized = (value || '').toLowerCase();
    if (['auto', 'number', 'text', 'percent'].indexOf(normalized) !== -1) {
      return normalized;
    }
    return 'auto';
  }

  normalizeValueSize(value: string) {
    const normalized = (value || '').toLowerCase();
    if (['small', 'medium', 'large'].indexOf(normalized) !== -1) {
      return normalized;
    }
    return 'medium';
  }

  normalizeTrendDirection(value: any) {
    const normalized = this.normalizeToString(value).toLowerCase();
    if (['up', 'positive', 'good', 'increase', 'success'].indexOf(normalized) !== -1) {
      return 'up';
    }
    if (['down', 'negative', 'bad', 'decrease', 'danger'].indexOf(normalized) !== -1) {
      return 'down';
    }
    return 'neutral';
  }

  inferTrendDirection(value: any) {
    const text = this.normalizeToString(value);
    if (text.indexOf('-') === 0) {
      return 'down';
    }
    if (text.indexOf('+') === 0) {
      return 'up';
    }
    return 'neutral';
  }

  getGridStyle(config: any) {
    const minWidth = config.minCardWidth + 'px';
    const map: any = {
      one: 'repeat(1, minmax(0, 1fr))',
      two: 'repeat(2, minmax(0, 1fr))',
      three: 'repeat(3, minmax(0, 1fr))',
      four: 'repeat(4, minmax(0, 1fr))',
      auto: 'repeat(auto-fit, minmax(' + minWidth + ', 1fr))',
    };
    return {
      gridTemplateColumns: map[config.layoutMode] || map.auto,
    };
  }

  getCardIcon(card: KpiCard) {
    const configured = this.normalizeToString(card.icon);
    if (configured) {
      return configured.substring(0, 3).toUpperCase();
    }
    const words = this.normalizeToString(card.title).split(/\s+/).filter(Boolean);
    if (words.length === 0) {
      return 'KPI';
    }
    if (words.length === 1) {
      return words[0].substring(0, 2).toUpperCase();
    }
    return (words[0].charAt(0) + words[1].charAt(0)).toUpperCase();
  }

  renderTrend(card: KpiCard, config: any) {
    if (!config.showTrend || !card.trend) {
      return null;
    }
    return (
      <span className={'kcs-trend kcs-trend-' + this.normalizeTrendDirection(card.trendDirection)}>
        {card.trend}
      </span>
    );
  }

  renderTarget(card: KpiCard) {
    if (!card.targetLabel && !card.targetValue) {
      return null;
    }
    return (
      <div className="kcs-target">
        {card.targetLabel ? <span>{card.targetLabel}</span> : null}
        {card.targetValue ? <strong>{card.targetValue}</strong> : null}
      </div>
    );
  }

  renderCard(card: KpiCard, index: number, config: any) {
    return (
      <div className={'kcs-card kcs-tone-' + card.tone} key={index}>
        <div className="kcs-card-top">
          <div>
            <div className="kcs-title">{card.title}</div>
            {card.subtitle ? <div className="kcs-subtitle">{card.subtitle}</div> : null}
          </div>
          {config.showIcon ? <div className="kcs-icon">{this.getCardIcon(card)}</div> : null}
        </div>
        <div className={'kcs-value kcs-value-' + config.valueSize}>{this.formatValue(card, config)}</div>
        <div className="kcs-card-bottom">
          {this.renderTrend(card, config)}
          {this.renderTarget(card)}
        </div>
      </div>
    );
  }

  renderEmptyState(config: any) {
    return (
      <div className="kcs-empty">
        <div className="kcs-empty-title">{config.emptyStateText}</div>
        <div className="kcs-empty-help">为 cardsConfig 配置指标数据后，即可显示卡片。</div>
      </div>
    );
  }

  render() {
    const config = this.getConfig();
    const rootClass = 'kpi-card-set' + (config.compactMode ? ' kcs-compact' : '');
    const styles = [
      '.kpi-card-set{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;color:#172033;width:100%;box-sizing:border-box;}',
      '.kcs-header{margin:0 0 14px 0;}',
      '.kcs-heading{font-size:18px;line-height:1.3;font-weight:700;color:#111827;margin:0;}',
      '.kcs-support{font-size:13px;line-height:1.45;color:#64748b;margin:4px 0 0 0;}',
      '.kcs-grid{display:grid;gap:14px;width:100%;}',
      '.kcs-card{position:relative;overflow:hidden;min-height:138px;border:1px solid #d9e2ef;border-radius:8px;background:#fff;padding:16px;box-sizing:border-box;box-shadow:0 1px 2px rgba(15,23,42,0.04);}',
      '.kcs-card:before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:#2f73ff;}',
      '.kcs-card-top{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;}',
      '.kcs-title{font-size:13px;line-height:1.35;font-weight:650;color:#344054;}',
      '.kcs-subtitle{font-size:12px;line-height:1.4;color:#7a8699;margin-top:3px;}',
      '.kcs-icon{flex:0 0 auto;min-width:34px;height:28px;border-radius:7px;background:#eef4ff;border:1px solid #cfe0ff;color:#185abc;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:750;letter-spacing:0;}',
      '.kcs-value{font-weight:750;color:#0f172a;margin-top:18px;line-height:1.05;letter-spacing:0;}',
      '.kcs-value-small{font-size:24px;}',
      '.kcs-value-medium{font-size:30px;}',
      '.kcs-value-large{font-size:38px;}',
      '.kcs-card-bottom{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:24px;margin-top:12px;}',
      '.kcs-trend{display:inline-flex;align-items:center;min-height:24px;border-radius:999px;padding:0 9px;font-size:12px;font-weight:700;border:1px solid transparent;}',
      '.kcs-trend-up{color:#067647;background:#ecfdf3;border-color:#abefc6;}',
      '.kcs-trend-down{color:#b42318;background:#fef3f2;border-color:#fecdca;}',
      '.kcs-trend-neutral{color:#475467;background:#f8fafc;border-color:#e4e7ec;}',
      '.kcs-target{display:flex;align-items:baseline;gap:5px;font-size:12px;color:#667085;min-width:0;}',
      '.kcs-target strong{font-size:12px;color:#344054;font-weight:700;white-space:nowrap;}',
      '.kcs-tone-blue:before{background:#2f73ff;}.kcs-tone-blue .kcs-icon{background:#eef4ff;border-color:#cfe0ff;color:#185abc;}',
      '.kcs-tone-green:before{background:#12b76a;}.kcs-tone-green .kcs-icon{background:#ecfdf3;border-color:#abefc6;color:#067647;}',
      '.kcs-tone-amber:before{background:#f79009;}.kcs-tone-amber .kcs-icon{background:#fffaeb;border-color:#fedf89;color:#b54708;}',
      '.kcs-tone-red:before{background:#f04438;}.kcs-tone-red .kcs-icon{background:#fef3f2;border-color:#fecdca;color:#b42318;}',
      '.kcs-tone-violet:before{background:#7a5af8;}.kcs-tone-violet .kcs-icon{background:#f4f3ff;border-color:#d9d6fe;color:#5925dc;}',
      '.kcs-tone-slate:before{background:#667085;}.kcs-tone-slate .kcs-icon{background:#f8fafc;border-color:#e4e7ec;color:#475467;}',
      '.kcs-empty{border:1px dashed #cbd5e1;border-radius:8px;background:#f8fafc;padding:18px;color:#64748b;}',
      '.kcs-empty-title{font-size:14px;font-weight:700;color:#344054;}',
      '.kcs-empty-help{font-size:12px;line-height:1.5;margin-top:4px;}',
      '.kcs-compact .kcs-grid{gap:10px;}',
      '.kcs-compact .kcs-card{min-height:112px;padding:12px;}',
      '.kcs-compact .kcs-value{margin-top:12px;}',
      '@media(max-width:640px){.kcs-grid{grid-template-columns:1fr!important;}.kcs-value-large{font-size:32px;}.kcs-card{min-height:auto;}.kcs-card-bottom{align-items:flex-start;flex-direction:column;}}',
    ].join('');

    return (
      <div className={rootClass}>
        <style>{styles}</style>
        {config.titleText || config.subtitleText ? (
          <div className="kcs-header">
            {config.titleText ? <div className="kcs-heading">{config.titleText}</div> : null}
            {config.subtitleText ? <div className="kcs-support">{config.subtitleText}</div> : null}
          </div>
        ) : null}
        {config.cards.length > 0 ? (
          <div className="kcs-grid" style={this.getGridStyle(config)}>
            {config.cards.map((card: KpiCard, index: number) => this.renderCard(card, index, config))}
          </div>
        ) : this.renderEmptyState(config)}
      </div>
    );
  }
}
