import React from 'react';
import { act } from 'react-dom/test-utils';
import {
  WorkflowDispatchContext,
  WorkflowStateContext,
} from '../../../../../contexts/Workflow';
import {
  waitForElement,
  mountWithContexts,
} from '../../../../../../testUtils/enzymeHelpers';
import {
  InventorySourcesAPI,
  JobTemplatesAPI,
  ProjectsAPI,
  WorkflowJobTemplatesAPI,
} from '../../../../../api';
import NodeModal from './NodeModal';

jest.mock('../../../../../api/models/Credentials');
jest.mock('../../../../../api/models/Inventories');
jest.mock('../../../../../api/models/InventorySources');
jest.mock('../../../../../api/models/JobTemplates');
jest.mock('../../../../../api/models/Projects');
jest.mock('../../../../../api/models/WorkflowJobTemplates');

let wrapper;
const dispatch = jest.fn();
const onSave = jest.fn();

const jtLaunchConfig = {
  can_start_without_user_input: false,
  passwords_needed_to_start: [],
  ask_scm_branch_on_launch: false,
  ask_variables_on_launch: true,
  ask_tags_on_launch: true,
  ask_diff_mode_on_launch: true,
  ask_skip_tags_on_launch: true,
  ask_job_type_on_launch: true,
  ask_limit_on_launch: false,
  ask_verbosity_on_launch: true,
  ask_inventory_on_launch: true,
  ask_credential_on_launch: true,
  survey_enabled: true,
  variables_needed_to_start: ['a'],
  credential_needed_to_start: false,
  inventory_needed_to_start: false,
  job_template_data: {
    name: 'A User-2 has admin permission',
    id: 25,
    description: '',
  },
  defaults: {
    extra_vars: '---',
    diff_mode: false,
    limit: '',
    job_tags: '',
    skip_tags: '',
    job_type: 'run',
    verbosity: 0,
    inventory: {
      name: ' Inventory 1 Org 0',
      id: 1,
    },
    credentials: [
      {
        id: 2,
        name: ' Credential 2 User 1',
        credential_type: 1,
        passwords_needed: [],
      },
      {
        id: 8,
        name: 'vault cred',
        credential_type: 3,
        passwords_needed: [],
        vault_id: '',
      },
    ],
    scm_branch: '',
  },
};

const mockJobTemplate = {
  id: 1,
  name: 'Test Job Template',
  type: 'job_template',
  url: '/api/v2/job_templates/1',
  summary_fields: {
    inventory: {
      name: 'Foo Inv',
      id: 1,
    },
    recent_jobs: [],
  },
  related: { webhook_receiver: '' },
  inventory: 1,
};

describe('NodeModal', () => {
  beforeAll(() => {
    JobTemplatesAPI.read.mockResolvedValue({
      data: {
        count: 1,
        results: [mockJobTemplate],
      },
    });
    JobTemplatesAPI.readOptions.mockResolvedValue({
      data: {
        actions: {
          GET: {},
          POST: {},
        },
        related_search_fields: [],
      },
    });
    JobTemplatesAPI.readLaunch.mockResolvedValue({ data: jtLaunchConfig });
    JobTemplatesAPI.readSurvey.mockResolvedValue({
      data: {
        name: '',
        description: '',
        spec: [
          {
            question_name: 'Foo',
            required: true,
            variable: 'bar',
            type: 'text',
            default: 'answer',
          },
        ],
        type: 'text',
        variable: 'bar',
      },
    });
    ProjectsAPI.read.mockResolvedValue({
      data: {
        count: 1,
        results: [
          {
            id: 1,
            name: 'Test Project',
            type: 'project',
            url: '/api/v2/projects/1',
          },
        ],
      },
    });
    ProjectsAPI.readOptions.mockResolvedValue({
      data: {
        actions: {
          GET: {},
          POST: {},
        },
        related_search_fields: [],
      },
    });
    InventorySourcesAPI.read.mockResolvedValue({
      data: {
        count: 1,
        results: [
          {
            id: 1,
            name: 'Test Inventory Source',
            type: 'inventory_source',
            url: '/api/v2/inventory_sources/1',
          },
        ],
      },
    });
    InventorySourcesAPI.readOptions.mockResolvedValue({
      data: {
        actions: {
          GET: {},
          POST: {},
        },
        related_search_fields: [],
      },
    });
    WorkflowJobTemplatesAPI.read.mockResolvedValue({
      data: {
        count: 1,
        results: [
          {
            id: 1,
            name: 'Test Workflow Job Template',
            type: 'workflow_job_template',
            url: '/api/v2/workflow_job_templates/1',
          },
        ],
      },
    });
    WorkflowJobTemplatesAPI.readOptions.mockResolvedValue({
      data: {
        actions: {
          GET: {},
          POST: {},
        },
        related_search_fields: [],
      },
    });
  });
  WorkflowJobTemplatesAPI.readLaunch.mockResolvedValue({
    data: {
      ask_inventory_on_launch: false,
      ask_limit_on_launch: false,
      ask_scm_branch_on_launch: false,
      can_start_without_user_input: false,
      defaults: {
        extra_vars: '---',
        inventory: {
          name: null,
          id: null,
        },
        limit: '',
        scm_branch: '',
      },
      survey_enabled: false,
      variables_needed_to_start: [],
      node_templates_missing: [],
      node_prompts_rejected: [272, 273],
      workflow_job_template_data: {
        name: 'jt',
        id: 53,
        description: '',
      },
      ask_variables_on_launch: false,
    },
  });
  afterAll(() => {
    jest.clearAllMocks();
  });
  describe('Add new node', () => {
    beforeEach(async () => {
      await act(async () => {
        wrapper = mountWithContexts(
          <WorkflowDispatchContext.Provider value={dispatch}>
            <WorkflowStateContext.Provider
              value={{
                nodeToEdit: null,
              }}
            >
              <NodeModal askLinkType onSave={onSave} title="Add Node" />
            </WorkflowStateContext.Provider>
          </WorkflowDispatchContext.Provider>
        );
      });
      await waitForElement(wrapper, 'PFWizard');
    });

    afterEach(() => {
      wrapper.unmount();
      onSave.mockClear();
    });

    test('Can successfully create a new job template node', async () => {
      act(() => {
        wrapper.find('#link-type-always').simulate('click');
      });
      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('Radio').simulate('click');
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });

      wrapper.update();

      expect(JobTemplatesAPI.readLaunch).toBeCalledWith(1);
      expect(JobTemplatesAPI.readSurvey).toBeCalledWith(25);
      wrapper.update();
      expect(wrapper.find('NodeNextButton').prop('buttonText')).toBe('Next');
      act(() => {
        wrapper.find('StepName#preview-step').simulate('click');
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });

      wrapper.update();

      expect(JobTemplatesAPI.readLaunch).toBeCalledWith(1);
      expect(JobTemplatesAPI.readSurvey).toBeCalledWith(25);
      wrapper.update();
      expect(wrapper.find('NodeNextButton').prop('buttonText')).toBe('Save');
      act(() => {
        wrapper.find('NodeNextButton').prop('onClick')();
      });
      wrapper.update();

      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      expect(onSave).toBeCalledWith(
        {
          linkType: 'always',
          nodeType: 'job_template',
          inventory: { name: 'Foo Inv', id: 1 },
          credentials: [],
          job_type: '',
          verbosity: '0',
          job_tags: '',
          skip_tags: '',
          diff_mode: false,
          survey_bar: 'answer',
          nodeResource: mockJobTemplate,
          extra_data: { bar: 'answer' },
        },
        jtLaunchConfig
      );
    });

    test('Can successfully create a new project sync node', async () => {
      act(() => {
        wrapper.find('#link-type-failure').simulate('click');
      });
      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('AnsibleSelect').prop('onChange')(null, 'project');
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('Radio').simulate('click');
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      expect(onSave).toBeCalledWith(
        {
          linkType: 'failure',
          nodeResource: {
            id: 1,
            name: 'Test Project',
            type: 'project',
            url: '/api/v2/projects/1',
          },
          nodeType: 'project',
          verbosity: undefined,
        },
        {}
      );
    });

    test('Can successfully create a new inventory source sync node', async () => {
      act(() => {
        wrapper.find('#link-type-failure').simulate('click');
      });
      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('AnsibleSelect').prop('onChange')(
          null,
          'inventory_source'
        );
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('Radio').simulate('click');
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      expect(onSave).toBeCalledWith(
        {
          linkType: 'failure',
          nodeResource: {
            id: 1,
            name: 'Test Inventory Source',
            type: 'inventory_source',
            url: '/api/v2/inventory_sources/1',
          },
          nodeType: 'inventory_source',
          verbosity: undefined,
        },
        {}
      );
    });

    test('Can successfully create a new workflow job template node', async () => {
      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('AnsibleSelect').prop('onChange')(
          null,
          'workflow_job_template'
        );
      });
      wrapper.update();
      await act(async () => wrapper.find('Radio').simulate('click'));
      wrapper.update();

      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      wrapper.update();

      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      expect(onSave).toBeCalledWith(
        {
          linkType: 'success',
          nodeResource: {
            id: 1,
            name: 'Test Workflow Job Template',
            type: 'workflow_job_template',
            url: '/api/v2/workflow_job_templates/1',
          },
          nodeType: 'workflow_job_template',
          verbosity: undefined,
        },
        {
          ask_inventory_on_launch: false,
          ask_limit_on_launch: false,
          ask_scm_branch_on_launch: false,
          ask_variables_on_launch: false,
          can_start_without_user_input: false,
          defaults: {
            extra_vars: '---',
            inventory: { id: null, name: null },
            limit: '',
            scm_branch: '',
          },
          node_prompts_rejected: [272, 273],
          node_templates_missing: [],
          survey_enabled: false,
          variables_needed_to_start: [],
          workflow_job_template_data: { description: '', id: 53, name: 'jt' },
        }
      );
    });

    test('Can successfully create a new approval template node', async () => {
      act(() => {
        wrapper.find('#link-type-always').simulate('click');
      });
      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      wrapper.update();
      await act(async () => {
        wrapper.find('AnsibleSelect').prop('onChange')(
          null,
          'workflow_approval_template'
        );
      });
      wrapper.update();

      await act(async () => {
        wrapper.find('input#approval-name').simulate('change', {
          target: { value: 'Test Approval', name: 'approvalName' },
        });
        wrapper.find('input#approval-description').simulate('change', {
          target: {
            value: 'Test Approval Description',
            name: 'approvalDescription',
          },
        });
        wrapper.find('input#approval-timeout-minutes').simulate('change', {
          target: { value: 5, name: 'timeoutMinutes' },
        });
        wrapper.find('input#approval-timeout-seconds').simulate('change', {
          target: { value: 30, name: 'timeoutSeconds' },
        });
      });
      wrapper.update();

      expect(wrapper.find('input#approval-name').prop('value')).toBe(
        'Test Approval'
      );
      expect(wrapper.find('input#approval-description').prop('value')).toBe(
        'Test Approval Description'
      );
      expect(wrapper.find('input#approval-timeout-minutes').prop('value')).toBe(
        5
      );
      expect(wrapper.find('input#approval-timeout-seconds').prop('value')).toBe(
        30
      );

      await act(async () => {
        wrapper.find('button#next-node-modal').simulate('click');
      });
      expect(onSave).toBeCalledWith(
        {
          approvalDescription: 'Test Approval Description',
          approvalName: 'Test Approval',
          linkType: 'always',
          nodeResource: null,
          nodeType: 'workflow_approval_template',
          timeoutMinutes: 5,
          timeoutSeconds: 30,
        },
        {}
      );
    });

    test('Cancel button dispatches as expected', () => {
      wrapper.find('button#cancel-node-modal').simulate('click');
      expect(dispatch).toHaveBeenCalledWith({
        type: 'CANCEL_NODE_MODAL',
      });
    });
  });
  describe('Edit existing node', () => {
    let newWrapper;
    afterEach(() => {
      newWrapper.unmount();
      jest.clearAllMocks();
    });

    test('Can successfully change project sync node to workflow approval node', async () => {
      await act(async () => {
        newWrapper = mountWithContexts(
          <WorkflowDispatchContext.Provider value={dispatch}>
            <WorkflowStateContext.Provider
              value={{
                nodeToEdit: {
                  id: 2,
                  fullUnifiedJobTemplate: {
                    id: 1,
                    name: 'Test Project',
                    type: 'project',
                  },
                },
              }}
            >
              <NodeModal
                askLinkType={false}
                onSave={onSave}
                title="Edit Node"
              />
            </WorkflowStateContext.Provider>
          </WorkflowDispatchContext.Provider>
        );
      });
      await waitForElement(newWrapper, 'PFWizard');
      newWrapper.update();
      expect(newWrapper.find('AnsibleSelect').prop('value')).toBe('project');
      await act(async () => {
        newWrapper.find('AnsibleSelect').prop('onChange')(
          null,
          'workflow_approval_template'
        );
      });
      newWrapper.update();
      await act(async () => {
        newWrapper.find('input#approval-name').simulate('change', {
          target: { value: 'Test Approval', name: 'approvalName' },
        });
        newWrapper.find('input#approval-description').simulate('change', {
          target: {
            value: 'Test Approval Description',
            name: 'approvalDescription',
          },
        });
        newWrapper.find('input#approval-timeout-minutes').simulate('change', {
          target: { value: 5, name: 'timeoutMinutes' },
        });
        newWrapper.find('input#approval-timeout-seconds').simulate('change', {
          target: { value: 30, name: 'timeoutSeconds' },
        });
      });
      newWrapper.update();

      expect(newWrapper.find('input#approval-name').prop('value')).toBe(
        'Test Approval'
      );
      expect(newWrapper.find('input#approval-description').prop('value')).toBe(
        'Test Approval Description'
      );
      expect(
        newWrapper.find('input#approval-timeout-minutes').prop('value')
      ).toBe(5);
      expect(
        newWrapper.find('input#approval-timeout-seconds').prop('value')
      ).toBe(30);
      await act(async () => {
        newWrapper.find('button#next-node-modal').simulate('click');
      });

      expect(onSave).toBeCalledWith(
        {
          approvalDescription: 'Test Approval Description',
          approvalName: 'Test Approval',
          linkType: 'success',
          nodeResource: null,
          nodeType: 'workflow_approval_template',
          timeoutMinutes: 5,
          timeoutSeconds: 30,
        },
        {}
      );
    });

    test('Can successfully change approval node to workflow job template node', async () => {
      await act(async () => {
        newWrapper = mountWithContexts(
          <WorkflowDispatchContext.Provider value={dispatch}>
            <WorkflowStateContext.Provider
              value={{
                nodeToEdit: {
                  id: 2,
                  fullUnifiedJobTemplate: {
                    id: 1,
                    name: 'Test Approval',
                    description: 'Test Approval Description',
                    type: 'workflow_approval_template',
                    timeout: 0,
                  },
                },
              }}
            >
              <NodeModal
                askLinkType={false}
                onSave={onSave}
                title="Edit Node"
              />
            </WorkflowStateContext.Provider>
          </WorkflowDispatchContext.Provider>
        );
      });
      await waitForElement(newWrapper, 'PFWizard');
      expect(newWrapper.find('AnsibleSelect').prop('value')).toBe(
        'workflow_approval_template'
      );
      await act(async () => {
        newWrapper.find('AnsibleSelect').prop('onChange')(
          null,
          'workflow_job_template'
        );
      });
      newWrapper.update();
      await act(async () => newWrapper.find('Radio').simulate('click'));
      newWrapper.update();

      await act(async () => {
        newWrapper.find('button#next-node-modal').simulate('click');
      });
      newWrapper.update();
      await act(async () => {
        newWrapper.find('button#next-node-modal').simulate('click');
      });
      expect(onSave).toBeCalledWith(
        {
          linkType: 'success',
          nodeResource: {
            id: 1,
            name: 'Test Workflow Job Template',
            type: 'workflow_job_template',
            url: '/api/v2/workflow_job_templates/1',
          },
          nodeType: 'workflow_job_template',
        },
        {
          ask_inventory_on_launch: false,
          ask_limit_on_launch: false,
          ask_scm_branch_on_launch: false,
          ask_variables_on_launch: false,
          can_start_without_user_input: false,
          defaults: {
            extra_vars: '---',
            inventory: { id: null, name: null },
            limit: '',
            scm_branch: '',
          },
          node_prompts_rejected: [272, 273],
          node_templates_missing: [],
          survey_enabled: false,
          variables_needed_to_start: [],
          workflow_job_template_data: { description: '', id: 53, name: 'jt' },
        }
      );
    });
  });
});
