// @vitest-environment happy-dom
//
// "Show member timezones" puts each other member's current local time beside
// their name, in the same parenthesised style as the "(you)" tag on your own
// row — so the column reads `Mike (4:00 PM)`.
//
// A member with no timezone set renders nothing extra: we cannot know their
// local time and must not imply one. That is also why the toggle can look
// inert on a fresh server — nobody has set a timezone yet.
import { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const state = vi.hoisted(() => ({ showMemberTimezones: true, users: [], usersLoaded: true }));

vi.mock('../../context/ConnectionContext', () => ({
  useConnection: () => ({
    account: { username: '@alice', displayName: 'Alice', avatarUrl: null },
    api: { avatarUrl: () => null },
    updateAccount: vi.fn(),
  }),
}));

vi.mock('../../context/ServerDataContext', () => ({
  useServerData: () => ({ users: state.users, usersLoaded: state.usersLoaded }),
}));

vi.mock('../../context/SettingsContext', () => ({
  useSettings: () => ({ showMemberTimezones: state.showMemberTimezones }),
}));

import { MembersPanel } from './MembersPanel';

let container;
let currentRoot;

function member(username, displayName, timezone) {
  return {
    username,
    displayName,
    timezone,
    status: '',
    avatar: null,
    removed: false,
    lastSeenAt: null,
    devices: [],
  };
}

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
  state.showMemberTimezones = true;
  state.usersLoaded = true;
  state.users = [
    member('@alice', 'Alice', 'America/Los_Angeles'),
    member('@mike', 'Mike', 'Asia/Tokyo'),
    member('@nora', 'Nora', null),
  ];
});

afterEach(() => {
  if (currentRoot) act(() => currentRoot.unmount());
  currentRoot = null;
  document.body.removeChild(container);
  container = null;
});

function render() {
  const root = createRoot(container);
  currentRoot = root;
  act(() => root.render(<MembersPanel />));
  return root;
}

const zoneText = (name) =>
  [...container.querySelectorAll('.member-card')]
    .find((card) => card.querySelector('.member-name').textContent.startsWith(name))
    ?.querySelector('.member-timezone')?.textContent?.trim() ?? null;

describe('MembersPanel timezones', () => {
  it('parenthesises the time, matching the (you) tag style', () => {
    render();
    // Tokyo is a fixed offset, so the shape is stable regardless of when this runs.
    expect(zoneText('Mike')).toMatch(/^\(\d{1,2}:\d{2}(\s| )?(AM|PM)?\)$/i);
  });

  it('renders nothing for a member with no timezone set', () => {
    render();
    expect(zoneText('Nora')).toBeNull();
  });

  it('never shows a time on your own row — that row has the (you) tag', () => {
    render();
    expect(zoneText('Alice')).toBeNull();
    const own = [...container.querySelectorAll('.member-card')].find((c) =>
      c.querySelector('.member-name').textContent.startsWith('Alice')
    );
    expect(own.querySelector('.member-you-tag').textContent).toContain('(you)');
  });

  it('shows no times at all while the setting is off', () => {
    state.showMemberTimezones = false;
    render();
    expect(container.querySelectorAll('.member-timezone')).toHaveLength(0);
  });

  it('shows a loading hint only while the roster has never loaded', () => {
    state.users = [];
    state.usersLoaded = false;
    render();
    expect(container.textContent).toContain('Loading members…');

    state.usersLoaded = true;
    act(() => currentRoot.render(<MembersPanel />));
    expect(container.textContent).not.toContain('Loading members…');
  });
});
