import { render, screen, within } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import { RecordActivityFeedView, humanizeFieldName } from '../src/record-activity-feed';
import { parseCoreHistoryFields } from '../src/record-activity-feed-adapter';
import type { FeedEntry } from '../src/record-activity-feed-data';

// The feed view owns presentation only: it turns FeedEntry rows into the
// timeline. These cases pin the parts of that rendering a user would notice
// regressing — the humanized field headline and the un-struck before/after pair.

function fieldChange(overrides: Partial<FeedEntry> = {}): FeedEntry {
  return {
    id: 'fc-1',
    kind: 'field-change',
    timestampMs: Date.now() - 7 * 24 * 60 * 60 * 1000,
    actorName: 'Kenric Tran',
    title: 'updated this record',
    field: 'StageName',
    oldValue: 'Business Validation',
    newValue: 'Negotiation',
    ...overrides,
  };
}

describe('humanizeFieldName', () => {
  it('renders API names as readable field labels', () => {
    expect(humanizeFieldName('StageName')).toBe('Stage');
    expect(humanizeFieldName('CloseDate')).toBe('Close Date');
    expect(humanizeFieldName('Net_New_ARR__c')).toBe('Net New ARR');
    // Acronyms stay whole instead of splitting into single letters.
    expect(humanizeFieldName('NetNewARRAmount')).toBe('Net New ARR Amount');
  });
});

describe('RecordActivityFeedView', () => {
  it('leads a field change with the field label, not the repeated actor sentence', () => {
    render(<RecordActivityFeedView entries={[fieldChange()]} loading={false} error={null} />);

    // The headline is the field; "updated this record" no longer appears at all.
    expect(screen.getByText('Stage')).toBeTruthy();
    expect(screen.queryByText(/updated this record/)).toBeNull();
    // The actor is demoted to the meta line alongside the relative time.
    expect(screen.getByText(/Kenric Tran · 7d ago/)).toBeTruthy();
  });

  it('shows the before and after values without striking the old one through', () => {
    const { container } = render(
      <RecordActivityFeedView entries={[fieldChange()]} loading={false} error={null} />,
    );

    expect(screen.getByText('Business Validation')).toBeTruthy();
    expect(screen.getByText('Negotiation')).toBeTruthy();
    expect(container.querySelector('.line-through')).toBeNull();
  });

  it('omits the arrow pair for a first-time set that has no prior value', () => {
    render(
      <RecordActivityFeedView
        entries={[fieldChange({ oldValue: undefined, newValue: 'Negotiation' })]}
        loading={false}
        error={null}
      />,
    );

    expect(screen.getByText('Negotiation')).toBeTruthy();
    expect(screen.queryByText('→')).toBeNull();
    expect(screen.queryByText('blank')).toBeNull();
  });

  it('opens a clickable activity row from the keyboard as well as the mouse', async () => {
    const onOpen = vi.fn();
    const entry: FeedEntry = {
      id: 'act-Task-1',
      kind: 'activity',
      timestampMs: Date.now(),
      title: 'Intro call',
      activity: { id: '00T1', objectApiName: 'Task' } as FeedEntry['activity'],
    };
    render(<RecordActivityFeedView entries={[entry]} loading={false} error={null} onOpen={onOpen} />);

    const row = screen.getByRole('button');
    expect((row as HTMLElement).tabIndex).toBe(0);

    const { fireEvent } = await import('@testing-library/react');
    fireEvent.keyDown(row, { key: 'Enter' });
    expect(onOpen).toHaveBeenCalledTimes(1);
  });

  it('renders a lifecycle sentinel as a readable label with no value pair', () => {
    render(
      <RecordActivityFeedView
        entries={[fieldChange({ field: '__created__', oldValue: undefined, newValue: undefined })]}
        loading={false}
        error={null}
      />,
    );

    // Salesforce surfaces creation readably; the raw token must never appear.
    expect(screen.getByText('Created')).toBeTruthy();
    expect(screen.queryByText(/__created__/)).toBeNull();
    expect(screen.queryByText('blank')).toBeNull();
    expect(screen.queryByText('→')).toBeNull();
  });

  it('renders the empty state when there is nothing to show', () => {
    const { container } = render(
      <RecordActivityFeedView entries={[]} loading={false} error={null} />,
    );

    expect(screen.getByText('No recent activity')).toBeTruthy();
    // No rail is drawn when there are no rows to connect.
    expect(within(container).queryByRole('list')).toBeNull();
  });
});

describe('parseCoreHistoryFields', () => {
  it('reads the authored list off panel config', () => {
    expect(parseCoreHistoryFields({ coreHistoryFields: ['StageName', 'CloseDate'] }))
      .toEqual(['StageName', 'CloseDate']);
  });

  it('falls back to undefined for absent or malformed config so the panel never blanks', () => {
    // Anything unusable must mean "show everything", not "show nothing".
    expect(parseCoreHistoryFields(undefined)).toBeUndefined();
    expect(parseCoreHistoryFields(null)).toBeUndefined();
    expect(parseCoreHistoryFields({})).toBeUndefined();
    expect(parseCoreHistoryFields({ coreHistoryFields: 'StageName' })).toBeUndefined();
    expect(parseCoreHistoryFields({ coreHistoryFields: [] })).toBeUndefined();
    expect(parseCoreHistoryFields({ coreHistoryFields: [null, 3, '  '] })).toBeUndefined();
  });

  it('drops unusable entries but keeps the valid ones', () => {
    expect(parseCoreHistoryFields({ coreHistoryFields: ['StageName', null, 7, '', 'CloseDate'] }))
      .toEqual(['StageName', 'CloseDate']);
  });
});
