package ru.zhenik.akka.example.infrastructure.discovery;

import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Props;
import com.google.common.net.HostAndPort;
import com.orbitz.consul.AgentClient;
import com.orbitz.consul.Consul;
import com.orbitz.consul.NotRegisteredException;
import com.orbitz.consul.model.agent.ImmutableRegistration;
import com.orbitz.consul.model.agent.Registration;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import ru.zhenik.akka.example.AppConfiguration;
import scala.concurrent.duration.FiniteDuration;

/**
 *
 * */
public class DiscoveryAgentActor extends AbstractActor {

  public static Props props(AppConfiguration config) {
    return Props.create(DiscoveryAgentActor.class, () -> new DiscoveryAgentActor(config));
  }

  private final AppConfiguration configuration;
  private final Consul consul;
  private final AgentClient agentClient;
  private final FiniteDuration SCHEDULED_WORK_DELAY;

  public DiscoveryAgentActor(AppConfiguration configuration){
    this.configuration = configuration;
    this.SCHEDULED_WORK_DELAY =  new FiniteDuration(configuration.serviceDiscoveryConfiguration.healthCheckTimeout, TimeUnit.SECONDS);

    // todo: terminate system if error occur while connecting to consul
    // get consul connection
    this.consul = Consul
        .builder()
        .withHostAndPort(
            HostAndPort.fromParts(
                configuration.serviceDiscoveryConfiguration.host,
                configuration.serviceDiscoveryConfiguration.port)
        )
        .build();

    // get agent
    agentClient = consul.agentClient();
    // set registration config
    Registration service = ImmutableRegistration.builder()
        .id(configuration.appId)
        .name(configuration.serviceName)
        .port(configuration.port)
        .address(configuration.host)
        .check(Registration.RegCheck.ttl(configuration.serviceDiscoveryConfiguration.healthCheckTimeout))
        .tags(Collections.singletonList("tag1"))
        .meta(Collections.singletonMap("version", "1.0"))
        .build();

    // register service
    agentClient.register(service);
    // check in with Consul, serviceId required only.  client will prepend "service:" for service level checks.
    // Note that you need to continually check in before the TTL expires, otherwise your service's state will be marked as "critical".
  }

  @Override
  public void preStart() {
    getSelf().tell("Do Scheduled Work", ActorRef.noSender());
  }

  @Override
  public Receive createReceive() {
    return receiveBuilder()
        .matchEquals("Do Scheduled Work", work -> {
          sendHealthCheck();
          context().system()
              // send each (10seconds default) health-check to consul
              .scheduler()
              .schedule(
                  // delay before 1st request
                  new FiniteDuration(5, TimeUnit.SECONDS),
                  SCHEDULED_WORK_DELAY,
                  healthCheck(),
                  getContext().dispatcher()
              );
        })
        .build();
  }

  private void sendHealthCheck() {
    try {
      agentClient.pass(configuration.appId, configuration.serviceName +" alive and reachable");
    } catch (NotRegisteredException e) {
      e.printStackTrace();
      getContext().getSystem().terminate();
    }
    System.out.println("Health check has been sent");
  }

  private Runnable healthCheck() {
    return this::sendHealthCheck;
  }

}