import {exportVariable} from '@actions/core'
import {mkdirP} from '@actions/io'
import child_process, {execFileSync, execSync} from 'child_process'
import {appendFileSync} from 'fs'
import {action, TestFlag} from '../src/constants'
import {execute} from '../src/execute'
import {configureSSH} from '../src/ssh'

const originalAction = JSON.stringify(action)

jest.mock('fs', () => ({
  appendFileSync: jest.fn(),
  existsSync: jest.fn()
}))

jest.mock('child_process', () => ({
  execFileSync: jest.fn(),
  execSync: jest.fn()
}))

jest.mock('@actions/io', () => ({
  rmRF: jest.fn(),
  mkdirP: jest.fn()
}))

jest.mock('@actions/core', () => ({
  setFailed: jest.fn(),
  getInput: jest.fn(),
  setOutput: jest.fn(),
  isDebug: jest.fn(),
  info: jest.fn(),
  exportVariable: jest.fn()
}))

jest.mock('../src/execute', () => ({
  execute: jest.fn(() => ({stdout: '', stderr: ''}))
}))

describe('configureSSH', () => {
  afterEach(() => {
    Object.assign(action, JSON.parse(originalAction))
  })

  it('should skip client configuration if sshKey is set to true', async () => {
    Object.assign(action, {
      hostname: 'github.com',
      silent: false,
      folder: 'assets',
      branch: 'branch',
      sshKey: true,
      pusher: {
        name: 'asd',
        email: 'as@cat'
      },
      isTest: TestFlag.HAS_CHANGED_FILES
    })

    await configureSSH(action)

    expect(execute).toHaveBeenCalledTimes(0)
    expect(mkdirP).toHaveBeenCalledTimes(0)
    expect(appendFileSync).toHaveBeenCalledTimes(0)
  })

  it('should configure the ssh client if a key is defined', async () => {
    ;(child_process.execFileSync as jest.Mock).mockImplementationOnce(() => {
      return 'SSH_AUTH_SOCK=/some/random/folder/agent.123; export SSH_AUTH_SOCK;\nSSH_AGENT_PID=123; export SSH_AGENT_PID;'
    })

    Object.assign(action, {
      hostname: 'github.com',
      silent: false,
      folder: 'assets',
      branch: 'branch',
      sshKey: '?=-----BEGIN 123 456\n 789',
      pusher: {
        name: 'asd',
        email: 'as@cat'
      },
      isTest: TestFlag.HAS_CHANGED_FILES
    })

    await configureSSH(action)

    expect(execFileSync).toHaveBeenCalledTimes(1)
    expect(exportVariable).toHaveBeenCalledTimes(2)
    expect(execSync).toHaveBeenCalledTimes(3)
  })

  it('should not export variables if the return from ssh-agent is skewed', async () => {
    ;(child_process.execFileSync as jest.Mock).mockImplementationOnce(() => {
      return 'useless nonsense here;'
    })

    Object.assign(action, {
      hostname: 'github.com',
      silent: false,
      folder: 'assets',
      branch: 'branch',
      sshKey: '?=-----BEGIN 123 456\n 789',
      pusher: {
        name: 'asd',
        email: 'as@cat'
      },
      isTest: TestFlag.HAS_CHANGED_FILES
    })

    await configureSSH(action)

    expect(execFileSync).toHaveBeenCalledTimes(1)
    expect(exportVariable).toHaveBeenCalledTimes(0)
    expect(execSync).toHaveBeenCalledTimes(3)
  })

  it('should throw if something errors', async () => {
    ;(child_process.execFileSync as jest.Mock).mockImplementationOnce(() => {
      throw new Error('Mocked throw')
    })

    Object.assign(action, {
      hostname: 'github.com',
      silent: false,
      folder: 'assets',
      branch: 'branch',
      sshKey: 'real_key',
      pusher: {
        name: 'asd',
        email: 'as@cat'
      },
      isTest: TestFlag.HAS_CHANGED_FILES
    })

    try {
      await configureSSH(action)
    } catch (error) {
      expect(error instanceof Error && error.message).toBe(
        'The ssh client configuration encountered an error: Mocked throw ❌'
      )
    }
  })
})
