import { AxiosResponse, AxiosError, AxiosInstance } from 'axios';
import { ScanStatus } from '@squiz/virus-scanner-lib';
import { execSync } from 'child_process';

export async function watchAndWaitForUploadAndScanComplete(
  apiClient: AxiosInstance,
  endpoint: string,
  id: string,
  managementURL?: string,
): Promise<void> {
  let poll: () => Promise<any>;
  if (managementURL) {
    poll = () => handleResponse<ScanStatus>(apiClient.get(endpoint + id, { baseURL: managementURL }));
  } else {
    poll = () => handleResponse<ScanStatus>(apiClient.get(endpoint + id));
  }
  return new Promise((resolve, reject) => {
    const recurse = () =>
      poll().then(async (req) => {
        if (req.status == 'Success') {
          resolve();
        } else if (req.status == 'Flagged') {
          reject(new Error('upload has been flagged as a virus'));
        } else if (req.status == 'Error') {
          reject(new Error('there has been an error'));
        } else {
          setTimeout(async () => {
            await recurse();
          }, 1000);
        }
      });

    recurse();
  });
}

export async function checkIfVersionExists(apiClient: AxiosInstance, endpoint: string, managementURL?: string) {
  try {
    const response = await apiClient.get(endpoint, {
      validateStatus: null,
      baseURL: managementURL,
    });

    if (response.status === 200) {
      return true;
    } else if (response.status === 404) {
      return false;
    }

    const error = new AxiosError(
      response?.data?.message || response.statusText,
      response.status.toString(),
      undefined,
      undefined,
      response,
    );

    throw handleError(error);
  } catch (error) {
    throw handleError(error);
  }
}

export async function handleResponse<T>(axiosInstance: Promise<AxiosResponse<T>>): Promise<T> {
  try {
    const response = await axiosInstance;
    return response.data;
  } catch (error) {
    throw handleError(error);
  }
}

export function handleError(error: any): Error {
  const { response } = error;
  const errorMessage = error.message;
  const newError = new Error(errorMessage || 'An error has occurred');

  if (isAxiosError(error)) {
    const errorCode = response.status ?? 'unknown';
    if (response?.data?.message) {
      newError.message = `Unexpected response code ${errorCode}. ${response.data.message}`.trim();
    } else {
      newError.message = `${errorMessage} (code: ${errorCode})`;
    }
  }

  newError.stack = error.stack;

  return newError;
}

export function isAxiosError(error: any): error is AxiosError {
  return error && error.isAxiosError === true;
}

export function isAxiosResponse(response: unknown): response is AxiosResponse {
  return response instanceof Object && Object.prototype.hasOwnProperty.call(response, 'status');
}

/**
 * Execute a shell command and return the trimmed result as a string.
 * @param {string} command - The shell command to execute.
 * @returns {string} The trimmed result of the shell command.
 */
function executeShellCommand(command: string): string {
  try {
    return execSync(command, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
  } catch (error) {
    return '';
  }
}

export function isUserInOrganization(organisationName: string = 'squiz'): boolean {
  const npmUsername = executeShellCommand('npm whoami');
  if (!npmUsername) {
    return false;
  }
  const orgListProcess = executeShellCommand(`npm org ls ${npmUsername} ${organisationName}`);
  const isMember = orgListProcess.includes('user');

  return isMember;
}
