/*
 * Copyright (c) 2023 Airbyte, Inc., all rights reserved.
 */

package io.airbyte.workers.internal;

import io.airbyte.commons.functional.CheckedConsumer;
import io.airbyte.config.WorkerDestinationConfig;
import io.airbyte.protocol.models.AirbyteMessage;
import java.nio.file.Path;
import java.util.Optional;

/**
 * This interface provides a java interface over all interactions with a Destination from the POV of
 * the platform. It encapsulates the full lifecycle of the Destination as well as any inputs and
 * outputs.
 */
public interface AirbyteDestination extends CheckedConsumer<AirbyteMessage, Exception>, AutoCloseable {

  /**
   * Starts the Destination container. It instantiates a writer to write to STDIN on that container.
   * It also instantiates a reader to listen on STDOUT.
   *
   * @param destinationConfig - contains the arguments that must be passed to the write method of the
   *        Destination.
   * @param jobRoot - directory where the job can write data.
   * @throws Exception - throws if there is any failure in startup.
   */
  void start(WorkerDestinationConfig destinationConfig, Path jobRoot) throws Exception;

  /**
   * Accepts an AirbyteMessage and writes it to STDIN of the Destination. Blocks if STDIN's buffer is
   * full.
   *
   * @param message message to send to destination.
   * @throws Exception - throws if there is any failure in writing to Destination.
   */
  @Override
  void accept(AirbyteMessage message) throws Exception;

  /**
   * This method is a flush to make sure all data that should be written to the Destination is
   * written. Any messages that have already been accepted
   * ({@link AirbyteDestination#accept(AirbyteMessage)} ()}) will be flushed. Any additional messages
   * sent to accept will not be flushed. In fact, flush should fail if the caller attempts to send it
   * additional messages after calling this method.
   *
   * (Potentially should just rename it to flush)
   *
   * @throws Exception - throws if there is any failure when flushing.
   */
  void notifyEndOfInput() throws Exception;

  /**
   * Means no more data will be emitted by the Destination. This may be because all data has already
   * been emitted or because the Destination container has exited.
   *
   * @return true, if no more data will be emitted. otherwise, false.
   */
  boolean isFinished();

  /**
   * Gets the exit value of the destination process. This should only be called after the destination
   * process has finished.
   *
   * @return exit code of the destination process
   * @throws IllegalStateException if the destination process has not exited
   */
  int getExitValue();

  /**
   * Attempts to read an AirbyteMessage from the Destination.
   *
   * @return returns an AirbyteMessage if the Destination emits one. Otherwise, empty. This method
   *         BLOCKS on waiting for the Destination to emit data to STDOUT.
   */
  Optional<AirbyteMessage> attemptRead();

  /**
   * Attempts to shut down the Destination's container. Waits for a graceful shutdown, capped by a
   * timeout.
   *
   * @throws Exception - throws if there is any failure in shutdown.
   */
  @Override
  void close() throws Exception;

  /**
   * Attempt to shut down the Destination's container quickly.
   *
   * @throws Exception - throws if there is any failure in shutdown.
   */
  void cancel() throws Exception;

}
