// @vitest-environment jsdom
import { cleanup, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { afterEach, describe, expect, it, vi } from 'vitest';

import { InstanceEditor, type InstanceDataSource, type ManagedInstance } from './instance-crud.js';

const owner: ManagedInstance = {
  id: 'owner',
  name: 'Owner modem',
  origin: 'https://owner.example',
  tags: ['west', 'lab'],
  revision: 3,
  credentialConfigured: true,
};

function dataSource(overrides: Partial<InstanceDataSource> = {}): InstanceDataSource {
  return {
    get: vi.fn(async () => owner),
    create: vi.fn(async () => owner),
    update: vi.fn(async () => ({ ...owner, revision: 4 })),
    testConnection: vi.fn(async () => ({ reachable: true, authenticated: true })),
    delete: vi.fn(async () => undefined),
    ...overrides,
  };
}

afterEach(cleanup);

describe('Instance CRUD form', () => {
  it('creates with tags and an explicit set-password action without rendering the secret', async () => {
    const user = userEvent.setup();
    const source = dataSource();
    render(<InstanceEditor mode="create" dataSource={source} />);

    await user.type(screen.getByLabelText('Name'), 'Lab modem');
    await user.type(screen.getByLabelText('Origin'), 'https://lab.example/admin');
    await user.type(screen.getByLabelText('Tags'), 'lab, west, lab');
    await user.selectOptions(screen.getByLabelText('Authentication method'), 'password');
    await user.type(screen.getByLabelText('Password'), 'do-not-render');
    expect(document.body.textContent).not.toContain('do-not-render');
    await user.click(screen.getByRole('button', { name: 'Add instance' }));

    expect(source.create).toHaveBeenCalledWith({
      name: 'Lab modem',
      origin: 'https://lab.example/admin',
      tags: ['lab', 'west'],
      password: { action: 'set', password: 'do-not-render' },
    });
  });

  it('loads only the route owner and preserves, sets, or clears its credential explicitly', async () => {
    const user = userEvent.setup();
    const source = dataSource();
    const { rerender } = render(
      <InstanceEditor mode="edit" instanceId="owner" dataSource={source} />,
    );
    expect(await screen.findByDisplayValue('Owner modem')).toBeTruthy();
    expect(source.get).toHaveBeenCalledWith('owner');

    await user.clear(screen.getByLabelText('Name'));
    await user.type(screen.getByLabelText('Name'), 'Renamed');
    await user.click(screen.getByRole('button', { name: 'Save changes' }));
    expect(source.update).toHaveBeenLastCalledWith(
      'owner',
      3,
      expect.objectContaining({ password: { action: 'preserve' } }),
    );

    await user.selectOptions(screen.getByLabelText('Password action'), 'clear');
    await user.click(screen.getByRole('button', { name: 'Save changes' }));
    expect(source.update).toHaveBeenLastCalledWith(
      'owner',
      4,
      expect.objectContaining({ password: { action: 'clear' } }),
    );

    rerender(<InstanceEditor mode="edit" instanceId="intruder" dataSource={source} />);
    expect((await screen.findByRole('alert')).textContent).toMatch(/does not match this route/i);
    expect(screen.queryByDisplayValue('Owner modem')).toBeNull();
  });

  it('tests the persisted owner connection and keeps safe errors visible while editing', async () => {
    const user = userEvent.setup();
    const source = dataSource({
      update: vi.fn(async () => {
        throw new Error('Could not save this instance.');
      }),
    });
    render(<InstanceEditor mode="edit" instanceId="owner" dataSource={source} />);
    await screen.findByDisplayValue('Owner modem');
    await user.click(screen.getByRole('button', { name: 'Test connection' }));
    expect((await screen.findByRole('status')).textContent).toMatch(/reachable and authenticated/i);

    await user.click(screen.getByRole('button', { name: 'Save changes' }));
    expect((await screen.findByRole('alert')).textContent).toContain(
      'Could not save this instance.',
    );
    await user.type(screen.getByLabelText('Name'), ' still here');
    expect(screen.getByRole('alert')).toBeTruthy();
  });

  it('requires an owner-bound typed confirmation before deletion', async () => {
    const user = userEvent.setup();
    const source = dataSource();
    render(<InstanceEditor mode="edit" instanceId="owner" dataSource={source} />);
    await screen.findByDisplayValue('Owner modem');
    await user.click(screen.getByRole('button', { name: 'Delete instance' }));
    expect(
      (screen.getByRole('button', { name: 'Confirm deletion' }) as HTMLButtonElement).disabled,
    ).toBe(true);
    await user.type(screen.getByLabelText('Type owner to confirm'), 'owner');
    await user.click(screen.getByRole('button', { name: 'Confirm deletion' }));
    expect(source.delete).toHaveBeenCalledWith('owner', 3);
  });
});
