import * as logSymbols from 'log-symbols';
import { CliTerseError, CliUsageError } from '@alwaysai/alwayscli';
import { checkProcessorBrand } from './destination-prompt-component';
import { targetPathPromptComponent } from './target-path-prompt-component';
import { targetProtocolPromptComponent } from './target-protocol-prompt-component';
import { writeTargetJsonFileComponent } from './write-target-json-file-component';
import {
  DOCKER_IMAGE_ID_INITIAL_VALUE,
  ALWAYSAI_CLI_EXECUTABLE_NAME
} from '../../../constants';
import {
  deviceCheckAndUpdateComponent,
  deviceSelectComponent,
  getTargetHostnameComponent
} from '../../device';
import { checkForDockerComponent } from '../../docker';
import { RequiredWithYesMessage, echo, logger } from '../../../util';
import {
  getTargetHardwareType,
  parseTargetHW,
  TargetHardware,
  TargetJsonFile,
  TargetPathDefaultValue,
  TargetProtocol
} from '../../../core/app';
import { addDeviceToProject, ProjectJsonFile } from '../../../core/project';
import { findOrWritePrivateKeyFileComponent } from '../../general';
import { ALWAYSAI_TARGET_HW_OVERRIDE } from '../../../environment';

export async function targetJsonComponent(props: {
  yes: boolean;
  targetProtocol?: TargetProtocol;
  targetHardware?: TargetHardware;
  targetHostname?: string;
  targetPath?: string;
  deviceId?: string;
}) {
  const { yes } = props;
  const currentTargetJson = TargetJsonFile();
  const currentTargetProtocol = currentTargetJson.readTargetProtocolSafe();
  let targetProtocol;
  if (yes) {
    targetProtocol = props.targetProtocol ?? currentTargetProtocol;
    if (!targetProtocol) {
      throw new CliUsageError(RequiredWithYesMessage('protocol'));
    }
    if (targetProtocol === 'native:' || targetProtocol === 'docker:') {
      if (!(await checkProcessorBrand())) {
        throw new CliUsageError(
          'edgeIQ is not supported on devices with Atom/Celeron processors.'
        );
      }
    }
  } else if (!props.targetProtocol) {
    targetProtocol = await targetProtocolPromptComponent({
      prevTargetProtocol: currentTargetProtocol
    });
  } else {
    targetProtocol = props.targetProtocol;
  }

  switch (targetProtocol) {
    case 'native:': {
      await writeTargetJsonFileComponent({
        targetProtocol
      });
      break;
    }

    case 'docker:': {
      await checkForDockerComponent();

      // Order of precedence: Command line > environment variable >
      // auto-determine based on system
      const targetHardware =
        props.targetHardware ??
        parseTargetHW(ALWAYSAI_TARGET_HW_OVERRIDE) ??
        (await getTargetHardwareType({}));
      echo(`${logSymbols.success} Set target hardware to ${targetHardware}`);

      await writeTargetJsonFileComponent({
        targetProtocol,
        dockerImageId: DOCKER_IMAGE_ID_INITIAL_VALUE,
        targetHardware
      });
      break;
    }

    case 'ssh+docker:': {
      await findOrWritePrivateKeyFileComponent({ yes });
      const projectJsonFile = ProjectJsonFile();
      const projectConfig = projectJsonFile.read().project;
      if (!projectConfig) {
        throw new CliTerseError(
          `App must have a project associated with it. Run '${ALWAYSAI_CLI_EXECUTABLE_NAME} app configure' to select a project.`
        );
      }
      const projectId = projectConfig.id;
      const deviceSelectOut = await deviceSelectComponent({
        yes,
        deviceId: props.deviceId
      });
      const device = deviceSelectOut.device;
      const isNewDevice = deviceSelectOut.new;

      const targetHostname = await getTargetHostnameComponent({
        yes,
        device,
        targetHostname: props.targetHostname
      });
      if (targetHostname === undefined) {
        throw new CliTerseError(
          'Cannot access selected device! The device hostname is unknown.'
        );
      }

      await deviceCheckAndUpdateComponent({
        yes,
        targetHostname,
        device,
        isNewDevice
      });

      await addDeviceToProject({ device, projectId });

      await checkForDockerComponent({ targetHostname });

      let targetPath =
        props.targetPath ??
        currentTargetJson.readFieldSafe({ name: 'targetPath' }) ??
        TargetPathDefaultValue();
      if (!yes) {
        targetPath = await targetPathPromptComponent({
          targetHostname,
          targetPath
        });
      }

      if (parseTargetHW(ALWAYSAI_TARGET_HW_OVERRIDE) !== undefined) {
        const msg = 'ALWAYSAI_TARGET_HW set but not used for remote device';
        echo(`${logSymbols.warning} ${msg}`);
        logger.warn(msg);
      }
      const targetHardware =
        props.targetHardware ??
        (await getTargetHardwareType({ targetHostname }));
      echo(`${logSymbols.success} Set target hardware to ${targetHardware}`);

      TargetJsonFile().write({
        targetProtocol,
        targetHostname,
        targetPath,
        dockerImageId: DOCKER_IMAGE_ID_INITIAL_VALUE,
        targetHardware,
        deviceId: device.uuid
      });
      break;
    }

    default:
      throw new Error('Unsupported protocol');
  }
}
