import React from 'react';
import type { FeedEntry, FeedEntryKind } from './record-activity-feed-data';

// Presentational "Most Recent Activity" feed.
//
// Layout is a timeline rail: one continuous hairline down the left with a
// per-kind dot sitting on it, so the rows read as a sequence rather than as a
// stack of separate cards. Every row is the same three-part shape —
//
//   headline   what changed / what happened   (the scannable line)
//   detail     the values, or the activity type
//   meta       actor · relative time          (subordinate, one line)
//
// The previous version led with "<Actor> updated this record" on every field
// change, which repeated the same nine words down the whole panel and pushed the
// actual field into small grey text. The field is the information; the actor is
// metadata, so they swap places here.

// Where a row's dot sits, measured from the top of its <li>: the row's own
// py-2.5 (10px) + the dot's mt-1 (4px) + half the h-2 dot (4px) = 18px. Kept as
// named constants so the rail below stays pinned to the dot if that padding is
// ever retuned.
const DOT_CENTER_FROM_ROW_TOP = 'top-[18px]';
const DOT_CENTER_HEIGHT = 'h-[18px]';

const KIND_DOT: Record<FeedEntryKind, string> = {
  'field-change': 'bg-blue-500',
  activity: 'bg-emerald-500',
  post: 'bg-violet-500',
};

// Field labels differing from a plain de-camel-casing of the API name. Kept
// deliberately tiny — this is a display nicety, not a metadata layer. The panel
// has no describe access (it is a src/-root module reading raw history rows), so
// anything absent here falls back to humanizeFieldName below.
const FIELD_LABEL_ALIASES: Readonly<Record<string, string>> = {
  StageName: 'Stage',
  OwnerId: 'Owner',
};

// Record-lifecycle sentinels rendered as readable text instead of the raw audit
// token. Salesforce surfaces record creation in its feed as a readable entry
// too, so labelling matches the reference behaviour rather than diverging from
// it. Anything not listed falls through to humanizeFieldName.
const SENTINEL_LABELS: Readonly<Record<string, string>> = {
  __created__: 'Created',
  __deleted__: 'Deleted',
  __undeleted__: 'Restored',
  __locked__: 'Locked',
  __unlocked__: 'Unlocked',
};

export function isStructuralField(field: string): boolean {
  return Object.prototype.hasOwnProperty.call(SENTINEL_LABELS, field);
}

export function humanizeFieldName(field: string): string {
  const sentinel = SENTINEL_LABELS[field];
  if (sentinel) return sentinel;
  const alias = FIELD_LABEL_ALIASES[field];
  if (alias) return alias;
  return field
    .replace(/__c$/, '')
    .replace(/_/g, ' ')
    // Split camelCase, but keep acronyms whole: "NetNewARRAmount" → "Net New ARR Amount".
    .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
    .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
    .replace(/\s+/g, ' ')
    .trim();
}

function formatWhen(timestampMs: number): string {
  if (!timestampMs) return '';
  const diffMs = Date.now() - timestampMs;
  // Future-dated entry (e.g. a scheduled activity that slipped through): show
  // the absolute date rather than a negative diff that would read "just now".
  if (diffMs < 0) return new Date(timestampMs).toLocaleDateString();
  const minutes = Math.floor(diffMs / 60000);
  if (minutes < 1) return 'just now';
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.floor(hours / 24);
  if (days < 30) return `${days}d ago`;
  return new Date(timestampMs).toLocaleDateString();
}

// A single value in a change pair. The old value is muted and the new one is
// emphasized — carrying the before/after purely through weight and contrast.
// The previous design struck the old value through, which reads as "deleted"
// rather than "superseded" and turned every row into visual noise.
const ValueChip: React.FC<{ value?: string; emphasis: 'from' | 'to' }> = ({ value, emphasis }) => (
  <span
    className={
      emphasis === 'to'
        ? 'inline-block rounded px-1.5 py-0.5 bg-gray-100 text-gray-900 font-medium'
        : 'inline-block rounded px-1.5 py-0.5 bg-gray-50 text-gray-500'
    }
  >
    {value && value.length > 0 ? value : 'blank'}
  </span>
);

const FieldChangeDetail: React.FC<{ entry: FeedEntry }> = ({ entry }) => (
  <div className="mt-1 flex flex-wrap items-center gap-1.5 text-xs">
    {/* A first-time set has no prior value; showing "blank → x" there would be
        noise, so the arrow pair only appears once there is something to leave. */}
    {entry.oldValue ? (
      <>
        <ValueChip value={entry.oldValue} emphasis="from" />
        <span aria-hidden="true" className="text-gray-300">→</span>
      </>
    ) : null}
    <ValueChip value={entry.newValue} emphasis="to" />
  </div>
);

export interface RecordActivityFeedViewProps {
  entries: FeedEntry[];
  loading: boolean;
  error: string | null;
  onOpen?: (entry: FeedEntry) => void;
}

export const RecordActivityFeedView: React.FC<RecordActivityFeedViewProps> = ({
  entries,
  loading,
  error,
  onOpen,
}) => (
  <div className="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">
    <div className="px-5 py-3.5 border-b border-gray-100">
      <h2 className="text-sm font-semibold text-gray-900">Most Recent Activity</h2>
    </div>

    {loading && entries.length === 0 && (
      <div className="px-5 py-10 text-sm text-gray-400 text-center">Loading activity…</div>
    )}
    {!loading && error && entries.length === 0 && (
      <div className="px-5 py-10 text-sm text-red-500 text-center">{error}</div>
    )}
    {!loading && !error && entries.length === 0 && (
      <div className="px-5 py-10 text-sm text-gray-400 text-center">No recent activity</div>
    )}

    {entries.length > 0 && (
      <ol className="px-5 py-4">
        {entries.map((entry, index) => {
          const clickable = entry.kind === 'activity' && !!entry.activity;
          const open = clickable ? () => onOpen?.(entry) : undefined;
          const headline =
            entry.kind === 'field-change' && entry.field
              ? humanizeFieldName(entry.field)
              : entry.title;
          const when = formatWhen(entry.timestampMs);

          // Timeline rail, drawn per row rather than as one absolute line over
          // the list: measured from the row's own left edge it needs no magic
          // offset for the list padding, so it cannot drift out of alignment
          // with the dots. It runs the full row height and the dot's white ring
          // masks the crossing; the first and last rows are half-length so the
          // rail terminates at a dot instead of overshooting into the padding.
          const isFirst = index === 0;
          const isLast = index === entries.length - 1;
          const railSpan = isFirst && isLast
            ? null
            : `${isFirst ? DOT_CENTER_FROM_ROW_TOP : 'top-0'} ${isLast ? DOT_CENTER_HEIGHT : 'bottom-0'}`;

          return (
            <li key={entry.id} className="relative flex gap-3 py-2.5">
              {railSpan && (
                <span
                  aria-hidden="true"
                  className={`absolute left-1 w-px -translate-x-1/2 bg-gray-200 ${railSpan}`}
                />
              )}
              <span
                aria-hidden="true"
                className={`relative z-10 mt-1 h-2 w-2 shrink-0 rounded-full ring-4 ring-white ${KIND_DOT[entry.kind]}`}
              />
              <div
                className={`min-w-0 flex-1 rounded-lg -mx-1.5 px-1.5 py-0.5 ${
                  clickable ? 'cursor-pointer hover:bg-gray-50' : ''
                }`}
                onClick={open}
                onKeyDown={
                  clickable
                    ? event => {
                        // role="button" without keyboard activation is a keyboard
                        // trap for the row; mirror the click on Enter/Space.
                        if (event.key === 'Enter' || event.key === ' ') {
                          event.preventDefault();
                          open?.();
                        }
                      }
                    : undefined
                }
                role={clickable ? 'button' : undefined}
                tabIndex={clickable ? 0 : undefined}
              >
                <div className="text-sm font-medium text-gray-900 truncate">{headline}</div>

                {entry.kind === 'field-change' && entry.field && !isStructuralField(entry.field) && (
                  <FieldChangeDetail entry={entry} />
                )}
                {entry.body && <div className="mt-0.5 text-xs text-gray-500 truncate">{entry.body}</div>}

                <div className="mt-1 text-[11px] text-gray-400">
                  {entry.actorName ?? 'User'}
                  {when ? ` · ${when}` : ''}
                </div>
              </div>
            </li>
          );
        })}
      </ol>
    )}
  </div>
);
