import { Buffer } from 'buffer';
import {
  MAX_DESIRED_APP_ORDER_PRICE,
  MAX_DESIRED_WORKERPOOL_ORDER_PRICE,
} from '../config/config.js';
import { handleIfProtocolError, WorkflowError } from '../utils/errors.js';
import * as ipfs from '../utils/ipfs-service.js';
import {
  addressSchema,
  telegramContentSchema,
  positiveNumberSchema,
  labelSchema,
  throwIfMissing,
  senderNameSchema,
} from '../utils/validators.js';
import {
  PrepareTelegramCampaignParams,
  PrepareTelegramCampaignResponse,
} from './types.js';
import {
  DappAddressConsumer,
  DataProtectorConsumer,
  IExecConsumer,
  IpfsGatewayConfigConsumer,
  IpfsNodeConfigConsumer,
} from './internalTypes.js';

export type PrepareTelegramCampaign = typeof prepareTelegramCampaign;

export const prepareTelegramCampaign = async ({
  iexec = throwIfMissing(),
  dataProtector = throwIfMissing(),
  workerpoolAddress,
  dappAddress,
  ipfsNode,
  ipfsGateway,
  senderName,
  telegramContent,
  label,
  appMaxPrice = MAX_DESIRED_APP_ORDER_PRICE,
  workerpoolMaxPrice = MAX_DESIRED_WORKERPOOL_ORDER_PRICE,
  grantedAccesses,
  maxProtectedDataPerTask,
}: IExecConsumer &
  DappAddressConsumer &
  IpfsNodeConfigConsumer &
  IpfsGatewayConfigConsumer &
  DataProtectorConsumer &
  PrepareTelegramCampaignParams): Promise<PrepareTelegramCampaignResponse> => {
  try {
    const vWorkerpoolAddress = addressSchema()
      .label('workerpoolAddress')
      .validateSync(workerpoolAddress);
    const vSenderName = senderNameSchema()
      .label('senderName')
      .validateSync(senderName);
    const vTelegramContent = telegramContentSchema()
      .required()
      .label('telegramContent')
      .validateSync(telegramContent);
    const vLabel = labelSchema().label('label').validateSync(label);
    const vDappAddress = addressSchema()
      .required()
      .label('dappAddress')
      .validateSync(dappAddress);
    const vAppMaxPrice = positiveNumberSchema()
      .label('appMaxPrice')
      .validateSync(appMaxPrice);
    const vWorkerpoolMaxPrice = positiveNumberSchema()
      .label('workerpoolMaxPrice')
      .validateSync(workerpoolMaxPrice);
    const vMaxProtectedDataPerTask = positiveNumberSchema()
      .label('maxProtectedDataPerTask')
      .validateSync(maxProtectedDataPerTask);

    // TODO: factor this
    // Encrypt telegram content
    const telegramContentEncryptionKey = iexec.dataset.generateEncryptionKey();
    const encryptedFile = await iexec.dataset
      .encrypt(
        Buffer.from(vTelegramContent, 'utf8'),
        telegramContentEncryptionKey
      )
      .catch((e) => {
        throw new WorkflowError({
          message: 'Failed to encrypt message content',
          errorCause: e,
        });
      });
    // Push telegram message to IPFS
    const cid = await ipfs
      .add(encryptedFile, {
        ipfsNode,
        ipfsGateway,
      })
      .catch((e) => {
        throw new WorkflowError({
          message: 'Failed to upload encrypted telegram content',
          errorCause: e,
        });
      });
    const multiaddr = `/ipfs/${cid}`;
    // Prepare secrets for the requester
    // Use a positive integer as secret ID (required by iexec)
    // Using "1" as a fixed ID for the requester secret
    const requesterSecretId = 1;
    const secrets = {
      [requesterSecretId]: JSON.stringify({
        senderName: vSenderName,
        telegramContentMultiAddr: multiaddr,
        telegramContentEncryptionKey,
      }),
    };
    // TODO: end factor this

    const { bulkRequest: campaignRequest } =
      await dataProtector.prepareBulkRequest({
        app: vDappAddress,
        appMaxPrice: vAppMaxPrice,
        workerpoolMaxPrice: vWorkerpoolMaxPrice,
        workerpool: vWorkerpoolAddress,
        args: vLabel,
        inputFiles: [],
        secrets,
        bulkAccesses: grantedAccesses,
        maxProtectedDataPerTask: vMaxProtectedDataPerTask,
      });
    return { campaignRequest };
  } catch (error) {
    //  Protocol error detected, re-throwing as-is
    if ((error as any)?.isProtocolError === true) {
      throw error;
    }
    // Handle protocol errors - this will throw if it's an ApiCallError
    // handleIfProtocolError transforms ApiCallError into WorkflowError with isProtocolError=true
    handleIfProtocolError(error);
    // For all other errors
    throw new WorkflowError({
      message: 'Failed to prepareTelegramCampaign',
      errorCause: error,
    });
  }
};
