import _ from 'lodash';
import { http, HttpResponse } from 'msw';
import { render } from '@testing-library/react';

import { createMockTeams, createMockUsers } from '@/react-tools/test-mocks';
import { server } from '@/setup-tests/server';
import { Role } from '@/portainer/users/types';
import { withUserProvider } from '@/react/test-utils/withUserProvider';
import { withTestRouter } from '@/react/test-utils/withRouter';
import { withTestQueryProvider } from '@/react/test-utils/withTestQuery';

import {
  ResourceControlOwnership,
  ResourceControlType,
  TeamResourceAccess,
  UserResourceAccess,
} from '../types';
import { ResourceControlViewModel } from '../models/ResourceControlViewModel';

import { AccessControlPanelDetails } from './AccessControlPanelDetails';

test.each([
  [ResourceControlOwnership.ADMINISTRATORS],
  [ResourceControlOwnership.PRIVATE],
  [ResourceControlOwnership.PUBLIC],
  [ResourceControlOwnership.RESTRICTED],
])(
  'when resource control with ownership %s is supplied, show its ownership',
  async (ownership) => {
    const resourceControl = buildViewModel(ownership);
    const { queryByLabelText } = await renderComponent(
      ResourceControlType.Container,
      resourceControl
    );

    expect(queryByLabelText('ownership')).toHaveTextContent(ownership);
  }
);

test('when resource control is not supplied, show administrators', async () => {
  const { queryByLabelText } = await renderComponent(
    ResourceControlType.Container
  );

  expect(queryByLabelText('ownership')).toHaveTextContent(
    ResourceControlOwnership.ADMINISTRATORS
  );
});

const inheritanceTests = [
  {
    resourceType: ResourceControlType.Container,
    parentType: ResourceControlType.Service,
  },
  {
    resourceType: ResourceControlType.Volume,
    parentType: ResourceControlType.Container,
  },
  ...[
    ResourceControlType.Config,
    ResourceControlType.Container,
    ResourceControlType.Network,
    ResourceControlType.Secret,
    ResourceControlType.Service,
    ResourceControlType.Volume,
  ].map((resourceType) => ({
    resourceType,
    parentType: ResourceControlType.Stack,
  })),
];

for (let i = 0; i < inheritanceTests.length; i += 1) {
  const { resourceType, parentType } = inheritanceTests[i];
  test(`when resource is ${ResourceControlType[resourceType]} and resource control is ${ResourceControlType[parentType]}, show message`, async () => {
    const resourceControl = buildViewModel(
      ResourceControlOwnership.ADMINISTRATORS,
      parentType
    );

    const { queryByLabelText } = await renderComponent(
      resourceType,
      resourceControl
    );
    const inheritanceMessage = queryByLabelText('inheritance-message');
    expect(inheritanceMessage).toBeVisible();
  });
}

test('when resource is limited to specific users, show number of users', async () => {
  const users = createMockUsers(10, Role.Standard);

  server.use(http.get('/api/users', () => HttpResponse.json(users)));

  const restrictedToUsers = _.sampleSize(users, 3);

  const resourceControl = buildViewModel(
    ResourceControlOwnership.RESTRICTED,
    ResourceControlType.Service,
    restrictedToUsers.map((user) => ({
      UserId: user.Id,
      AccessLevel: 1,
    }))
  );

  const { queryByText, findByLabelText } = await renderComponent(
    undefined,
    resourceControl
  );

  expect(queryByText(/Authorized users/)).toBeVisible();

  await expect(findByLabelText('authorized-users')).resolves.toHaveTextContent(
    `${restrictedToUsers.length} users`
  );
});

test('when resource is limited to specific teams, show comma separated list of their names', async () => {
  const teams = createMockTeams(10);

  server.use(http.get('/api/teams', () => HttpResponse.json(teams)));

  const restrictedToTeams = _.sampleSize(teams, 3);

  const resourceControl = buildViewModel(
    ResourceControlOwnership.RESTRICTED,
    ResourceControlType.Config,
    [],
    restrictedToTeams.map((team) => ({
      TeamId: team.Id,
      AccessLevel: 1,
    }))
  );

  const { queryByText, findByLabelText } = await renderComponent(
    undefined,
    resourceControl
  );

  expect(queryByText(/Authorized teams/)).toBeVisible();

  await expect(findByLabelText('authorized-teams')).resolves.toHaveTextContent(
    restrictedToTeams.map((team) => team.Name).join(', ')
  );
});

async function renderComponent(
  resourceType: ResourceControlType = ResourceControlType.Container,
  resourceControl?: ResourceControlViewModel
) {
  const Wrapped = withTestQueryProvider(
    withTestRouter(withUserProvider(AccessControlPanelDetails))
  );
  const queries = render(
    <Wrapped resourceControl={resourceControl} resourceType={resourceType} />
  );
  await expect(queries.findByText('Ownership')).resolves.toBeVisible();

  return queries;
}

function buildViewModel(
  ownership: ResourceControlOwnership,
  type: ResourceControlType = ResourceControlType.Config,
  users: UserResourceAccess[] = [],
  teams: TeamResourceAccess[] = []
): ResourceControlViewModel {
  return {
    Id: 0,
    Public: false,
    ResourceId: 0,
    System: false,
    TeamAccesses: teams,
    Ownership: ownership,
    Type: type,
    UserAccesses: users,
  };
}
