// @vitest-environment happy-dom
//
// The Login page's password field styling (`connection-field--remembered`)
// and its placeholder text must never disagree: both are derived from the
// same rememberLabel() call so there's a single source of truth for "is
// there actually something remembered to show". No react-testing-library in
// this repo (see FileTree.statuses.test.jsx) — mount with react-dom/client
// directly.
//
// '../../context/ConnectionContext' is mocked so the field can be driven
// straight off a fixed orgConnection/login without a real ConnectionProvider
// (see ConnectionContext.test.jsx for the fuller context-level suite).
import { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const state = vi.hoisted(() => ({ orgConnection: null, startupError: null }));

vi.mock('../../context/ConnectionContext', () => ({
  normalizeHandle: (value) => value,
  useConnection: () => ({
    loggedIn: false,
    orgConnection: state.orgConnection,
    startupError: state.startupError,
    login: vi.fn(),
    trustPrompt: null,
    pinMismatch: null,
    acceptTrust: vi.fn(),
    declineTrust: vi.fn(),
    forgetServerIdentity: vi.fn(),
    dismissPinMismatch: vi.fn(),
  }),
}));

import { ConnectionsModal } from './ConnectionsModal';

let container;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  document.body.removeChild(container);
  container = null;
  state.orgConnection = null;
  state.startupError = null;
});

function renderModal() {
  const root = createRoot(container);
  act(() => {
    root.render(<ConnectionsModal onClose={() => {}} />);
  });
  return root;
}

function passwordField() {
  return container.querySelector('input[type="password"]');
}

function errorText() {
  return container.querySelector('.connection-card-error')?.textContent ?? null;
}

describe('ConnectionsModal Login page password field', () => {
  it('is plain "Password" with no remembered styling when remember=true but the token already died (rememberExpiresAt: null)', () => {
    // Matches the post-password-change-then-restart state: the handler
    // deliberately leaves `remember` alone since the session stays open —
    // only the token died — so rememberExpiresAt goes null while
    // remember stays true.
    state.orgConnection = {
      remember: true,
      rememberExpiresAt: null,
      ip: '127.0.0.1:8666',
      username: '@alice',
    };
    const root = renderModal();
    try {
      const field = passwordField();
      expect(field.className).not.toContain('connection-field--remembered');
      expect(field.placeholder).toBe('Password');
    } finally {
      act(() => root.unmount());
    }
  });

  it('shows the italic remembered class and a "Remembered for N days" placeholder for a live future expiry', () => {
    const fiveDaysOut = Date.now() / 1000 + 5 * 86400;
    state.orgConnection = {
      remember: true,
      rememberExpiresAt: fiveDaysOut,
      ip: '127.0.0.1:8666',
      username: '@alice',
    };
    const root = renderModal();
    try {
      const field = passwordField();
      expect(field.className).toContain('connection-field--remembered');
      expect(field.placeholder).toBe('Remembered for 5 days');
    } finally {
      act(() => root.unmount());
    }
  });
});

describe('ConnectionsModal Login page after a failed startup resume', () => {
  // This page is the fallback the boot-time silent resume drops into. It has
  // to arrive carrying the reason, or the user is left wondering why they
  // weren't let straight in.
  it('shows the startup failure inline', () => {
    state.startupError = 'Your saved sign-in expired. Enter your password.';
    const root = renderModal();
    try {
      expect(errorText()).toBe('Your saved sign-in expired. Enter your password.');
    } finally {
      act(() => root.unmount());
    }
  });

  it('shows nothing when the app simply opened logged out', () => {
    const root = renderModal();
    try {
      expect(errorText()).toBeNull();
    } finally {
      act(() => root.unmount());
    }
  });
});
