/*
Copyright (C) 2025 QuantumNous

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

For commercial licensing, please contact support@quantumnous.com
*/

import React, { useEffect, useMemo, useRef, useState } from 'react';
import { Modal, Descriptions, Spin, Typography } from '@douyinfe/semi-ui';
import { API, showError, timestamp2string } from '../../../../helpers';

const { Text } = Typography;

function formatRate(hit, total) {
  if (!total || total <= 0) return '-';
  const r = (Number(hit || 0) / Number(total || 0)) * 100;
  if (!Number.isFinite(r)) return '-';
  return `${r.toFixed(2)}%`;
}

function formatTokenRate(n, d) {
  const nn = Number(n || 0);
  const dd = Number(d || 0);
  if (!dd || dd <= 0) return '-';
  const r = (nn / dd) * 100;
  if (!Number.isFinite(r)) return '-';
  return `${r.toFixed(2)}%`;
}

function formatCachedTokenRate(cachedTokens, promptTokens, mode) {
  if (mode === 'cached_over_prompt_plus_cached') {
    const denominator = Number(promptTokens || 0) + Number(cachedTokens || 0);
    return formatTokenRate(cachedTokens, denominator);
  }
  if (mode === 'cached_over_prompt') {
    return formatTokenRate(cachedTokens, promptTokens);
  }
  return '-';
}

function hasTextValue(value) {
  return typeof value === 'string' && value.trim() !== '';
}

const ChannelAffinityUsageCacheModal = ({
  t,
  showChannelAffinityUsageCacheModal,
  setShowChannelAffinityUsageCacheModal,
  channelAffinityUsageCacheTarget,
}) => {
  const [loading, setLoading] = useState(false);
  const [stats, setStats] = useState(null);
  const requestSeqRef = useRef(0);

  const params = useMemo(() => {
    const x = channelAffinityUsageCacheTarget || {};
    return {
      rule_name: (x.rule_name || '').trim(),
      using_group: (x.using_group || '').trim(),
      key_hint: (x.key_hint || '').trim(),
      key_fp: (x.key_fp || '').trim(),
    };
  }, [channelAffinityUsageCacheTarget]);

  useEffect(() => {
    if (!showChannelAffinityUsageCacheModal) {
      requestSeqRef.current += 1; // invalidate inflight request
      setLoading(false);
      setStats(null);
      return;
    }
    if (!params.rule_name || !params.key_fp) {
      setLoading(false);
      setStats(null);
      return;
    }

    const reqSeq = (requestSeqRef.current += 1);
    setStats(null);
    setLoading(true);
    (async () => {
      try {
        const res = await API.get('/api/log/channel_affinity_usage_cache', {
          params,
          disableDuplicate: true,
        });
        if (reqSeq !== requestSeqRef.current) return;
        const { success, message, data } = res.data || {};
        if (!success) {
          setStats(null);
          showError(t(message || '请求失败'));
          return;
        }
        setStats(data || {});
      } catch (e) {
        if (reqSeq !== requestSeqRef.current) return;
        setStats(null);
        showError(t('请求失败'));
      } finally {
        if (reqSeq !== requestSeqRef.current) return;
        setLoading(false);
      }
    })();
  }, [
    showChannelAffinityUsageCacheModal,
    params.rule_name,
    params.using_group,
    params.key_hint,
    params.key_fp,
    t,
  ]);

  const { rows, supportsTokenStats } = useMemo(() => {
    const s = stats || {};
    const hit = Number(s.hit || 0);
    const total = Number(s.total || 0);
    const windowSeconds = Number(s.window_seconds || 0);
    const lastSeenAt = Number(s.last_seen_at || 0);
    const promptTokens = Number(s.prompt_tokens || 0);
    const completionTokens = Number(s.completion_tokens || 0);
    const totalTokens = Number(s.total_tokens || 0);
    const cachedTokens = Number(s.cached_tokens || 0);
    const promptCacheHitTokens = Number(s.prompt_cache_hit_tokens || 0);
    const cachedTokenRateMode = String(s.cached_token_rate_mode || '').trim();
    const supportsTokenStats =
      cachedTokenRateMode === 'cached_over_prompt' ||
      cachedTokenRateMode === 'cached_over_prompt_plus_cached' ||
      cachedTokenRateMode === 'mixed';

    const data = [];
    const ruleName = String(s.rule_name || params.rule_name || '').trim();
    const usingGroup = String(s.using_group || params.using_group || '').trim();
    const keyHint = String(params.key_hint || '').trim();
    const keyFp = String(s.key_fp || params.key_fp || '').trim();

    if (hasTextValue(ruleName)) {
      data.push({ key: t('规则'), value: ruleName });
    }
    if (hasTextValue(usingGroup)) {
      data.push({ key: t('分组'), value: usingGroup });
    }
    if (hasTextValue(keyHint)) {
      data.push({ key: t('Key 摘要'), value: keyHint });
    }
    if (hasTextValue(keyFp)) {
      data.push({ key: t('Key 指纹'), value: keyFp });
    }
    if (windowSeconds > 0) {
      data.push({ key: t('TTL（秒）'), value: windowSeconds });
    }
    if (total > 0) {
      data.push({ key: t('命中率'), value: `${hit}/${total} (${formatRate(hit, total)})` });
    }
    if (lastSeenAt > 0) {
      data.push({ key: t('最近一次'), value: timestamp2string(lastSeenAt) });
    }

    if (supportsTokenStats) {
      if (promptTokens > 0) {
        data.push({ key: t('Prompt tokens'), value: promptTokens });
      }
      if (promptTokens > 0 || cachedTokens > 0) {
        data.push({
          key: t('Cached tokens'),
          value: `${cachedTokens} (${formatCachedTokenRate(cachedTokens, promptTokens, cachedTokenRateMode)})`,
        });
      }
      if (promptCacheHitTokens > 0) {
        data.push({ key: t('Prompt cache hit tokens'), value: promptCacheHitTokens });
      }
      if (completionTokens > 0) {
        data.push({ key: t('Completion tokens'), value: completionTokens });
      }
      if (totalTokens > 0) {
        data.push({ key: t('Total tokens'), value: totalTokens });
      }
    }

    return { rows: data, supportsTokenStats };
  }, [stats, params, t]);

  return (
    <Modal
      title={t('渠道亲和性：上游缓存命中')}
      visible={showChannelAffinityUsageCacheModal}
      onCancel={() => setShowChannelAffinityUsageCacheModal(false)}
      footer={null}
      centered
      closable
      maskClosable
      width={640}
    >
      <div style={{ padding: 16 }}>
        <div style={{ marginBottom: 12 }}>
          <Text type='tertiary' size='small'>
            {t(
              '命中判定：usage 中存在 cached tokens（例如 cached_tokens/prompt_cache_hit_tokens）即视为命中。',
            )}
            {' '}
            {t(
              'Cached tokens 占比口径由后端返回：Claude 语义按 cached/(prompt+cached)，其余按 cached/prompt。',
            )}
            {' '}
            {t('当前仅 OpenAI / Claude 语义支持缓存 token 统计，其他通道将隐藏 token 相关字段。')}
            {stats && !supportsTokenStats ? (
              <>
                {' '}
                {t('该记录不包含可用的 token 统计口径。')}
              </>
            ) : null}
          </Text>
        </div>
        <Spin spinning={loading} tip={t('加载中...')}>
          {stats && rows.length > 0 ? (
            <Descriptions data={rows} />
          ) : (
            <div style={{ padding: '24px 0' }}>
              <Text type='tertiary' size='small'>
                {loading ? t('加载中...') : t('暂无可展示数据')}
              </Text>
            </div>
          )}
        </Spin>
      </div>
    </Modal>
  );
};

export default ChannelAffinityUsageCacheModal;
