import React from 'react';

import type {
  PromptImpactStatus,
  PromptImpactWeekPoint,
} from '../../service/visibility/visibility.interface';

/** Plain-language explanation of how the per-prompt visibility score is built. */
export const VISIBILITY_SCORE_HELP =
  'How strongly AI surfaces you for THIS exact query (0-100), scored on its ' +
  'own - not a mention count, and not your overall reach. Weighted toward being ' +
  'mentioned at all and your site being cited, so one cited appearance here ' +
  'already scores high even while you are absent from other prompts. Your ' +
  'headline visibility blends all prompts, so a few strong scores can still mean ' +
  'low overall reach.';

/**
 * Decide how to show a trend's change. A score that climbed from 0 is a first
 * appearance, not a "+71 improvement", so it reads as "New" rather than a
 * delta - keeps the numbers honest across the card, modal and article panel.
 *
 * @param {number | null} baselineScore - Earliest score in the window (or the
 *     pre-publish baseline); ``null`` when unknown.
 * @param {number | null} currentScore - Latest score; ``null`` when no data.
 * @param {number | null} deltaScore - Precomputed delta; ``null`` when absent.
 * @returns {{kind: 'new'} | {kind: 'delta', value: number} | {kind: 'none'}}
 *     How to render the change.
 */
export function trendChange(
  baselineScore: number | null,
  currentScore: number | null,
  deltaScore: number | null
): { kind: 'new' } | { kind: 'delta'; value: number } | { kind: 'none' } {
  if (currentScore === null) return { kind: 'none' };
  if ((baselineScore === null || baselineScore === 0) && currentScore > 0) {
    return { kind: 'new' };
  }
  if (deltaScore !== null) return { kind: 'delta', value: deltaScore };

  return { kind: 'none' };
}

/** Props for {@link TrendStatusIcon}. */
interface TrendStatusIconProps {
  /** Trend bucket driving which glyph is drawn. */
  status: PromptImpactStatus;
  /** SVG edge length in px. */
  size?: number;
}

/**
 * Tiny inline status glyph (up / down / flat / collecting). Replaces the
 * icon-library icons used in the reference so this repo keeps zero icon-pack
 * dependencies; ``currentColor`` lets the parent tint it via the status class.
 *
 * @param {TrendStatusIconProps} props - Component props.
 * @returns {JSX.Element} The status icon SVG.
 */
function TrendStatusIcon({
  status,
  size = 14,
}: TrendStatusIconProps): JSX.Element {
  const common = {
    width: size,
    height: size,
    viewBox: '0 0 24 24',
    fill: 'none',
    stroke: 'currentColor',
    strokeWidth: 2,
    strokeLinecap: 'round' as const,
    strokeLinejoin: 'round' as const,
    'aria-hidden': true,
  };
  if (status === 'improving') {
    return (
      <svg {...common}>
        <polyline points="22 7 13.5 15.5 8.5 10.5 2 17" />
        <polyline points="16 7 22 7 22 13" />
      </svg>
    );
  }
  if (status === 'declining') {
    return (
      <svg {...common}>
        <polyline points="22 17 13.5 8.5 8.5 13.5 2 7" />
        <polyline points="16 17 22 17 22 11" />
      </svg>
    );
  }
  if (status === 'collecting') {
    return (
      <svg {...common} strokeDasharray="3 3">
        <circle cx="12" cy="12" r="9" />
      </svg>
    );
  }

  return (
    <svg {...common}>
      <line x1="5" y1="12" x2="19" y2="12" />
    </svg>
  );
}

/** Visual treatment per trend status: label and accent class. */
export const PROMPT_TREND_STATUS_META: Record<
  PromptImpactStatus,
  { label: string; className: string }
> = {
  improving: { label: 'improving', className: 'text-green-600' },
  declining: { label: 'declining', className: 'text-red-600' },
  flat: { label: 'flat', className: 'text-gray-500' },
  collecting: { label: 'collecting data', className: 'text-gray-500' },
};

/** Props for {@link PromptSparkline}. */
interface PromptSparklineProps {
  /** Weekly composite points, oldest first. */
  series: PromptImpactWeekPoint[];
  /** SVG width in px. */
  width?: number;
  /** SVG height in px. */
  height?: number;
  /** ISO week to dot on the line (e.g. an article's publish week). */
  markerWeek?: string | null;
}

/**
 * Tiny inline sparkline of composite scores (0-100), oldest to newest.
 * Returns ``null`` for fewer than two points - a single dot reads as noise.
 * Plots against the FIXED 0-100 scale (not the series' own min/max) so a
 * barely-moving prompt reads as a near-straight line, and optionally dots a
 * given week so a reader can see where publish sits on the trend.
 *
 * @param {PromptSparklineProps} props - Component props.
 * @returns {JSX.Element | null} The sparkline SVG, or ``null``.
 */
export function PromptSparkline({
  series,
  width = 72,
  height = 22,
  markerWeek = null,
}: PromptSparklineProps): JSX.Element | null {
  if (series.length < 2) return null;
  const verticalPadding = 2;
  const usableHeight = height - verticalPadding * 2;
  const stepX = width / (series.length - 1);
  const coords = series.map((point, index) => {
    const score = Math.max(0, Math.min(100, point.composite_score));

    return {
      x: index * stepX,
      y: verticalPadding + (1 - score / 100) * usableHeight,
      week: point.week_iso,
    };
  });
  const points = coords
    .map(coord => `${coord.x.toFixed(1)},${coord.y.toFixed(1)}`)
    .join(' ');
  let marker: { x: number; y: number } | null = null;
  if (markerWeek) {
    const exact = coords.findIndex(coord => coord.week === markerWeek);
    const index =
      exact >= 0
        ? exact
        : coords.findIndex(coord => (coord.week || '') >= markerWeek);
    if (index >= 0) marker = { x: coords[index].x, y: coords[index].y };
  }

  return (
    <svg
      width={width}
      height={height}
      viewBox={`0 0 ${width} ${height}`}
      style={{ overflow: 'visible' }}
      aria-hidden="true"
    >
      <polyline
        points={points}
        fill="none"
        stroke="currentColor"
        strokeWidth={1.5}
        strokeLinecap="round"
        strokeLinejoin="round"
      />
      {marker && (
        <circle cx={marker.x} cy={marker.y} r={2.5} fill="currentColor" />
      )}
    </svg>
  );
}

/** Props for {@link PromptTrendChip}. */
interface PromptTrendChipProps {
  /** Latest composite score, or ``null`` when no data yet. */
  currentScore: number | null;
  /** Delta vs the baseline week, or ``null`` until two weeks exist. */
  deltaScore: number | null;
  /** Trend bucket driving icon + colour. */
  status: PromptImpactStatus;
  /** Weekly points for the inline sparkline. */
  series: PromptImpactWeekPoint[];
}

/**
 * Compact "Visibility 64 /100 ▲+18 ▁▂▅▇" trend chip for a prompt card. Shows a
 * muted "collecting data" hint while a prompt has fewer than two scored weeks
 * so the merchant knows it is tracked but not yet charted.
 *
 * @param {PromptTrendChipProps} props - Component props.
 * @returns {JSX.Element} The chip element.
 */
export function PromptTrendChip({
  currentScore,
  deltaScore,
  status,
  series,
}: PromptTrendChipProps): JSX.Element {
  const meta = PROMPT_TREND_STATUS_META[status];

  if (currentScore === null) {
    return (
      <span className="inline-flex items-center gap-1 text-[11px] text-gray-500">
        <TrendStatusIcon status="collecting" size={12} />
        collecting data
      </span>
    );
  }

  const baselineScore = series.length ? series[0].composite_score : null;
  const change = trendChange(baselineScore, currentScore, deltaScore);

  return (
    <span
      className={`inline-flex items-center gap-1.5 text-xs ${
        change.kind === 'delta' ? meta.className : 'text-gray-500'
      }`}
    >
      <span className="text-gray-500">Visibility</span>
      <span className="font-semibold text-gray-900">{currentScore}</span>
      <span className="text-gray-500">/100</span>
      {change.kind === 'new' && (
        <span className="font-medium text-green-600">New</span>
      )}
      {change.kind === 'delta' && (
        <span className="inline-flex items-center gap-0.5 font-medium">
          <TrendStatusIcon status={status} size={14} />
          {`${change.value > 0 ? '+' : ''}${change.value}`}
        </span>
      )}
      <PromptSparkline series={series} />
    </span>
  );
}

export { TrendStatusIcon };
