/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.cassandrareaper.core;

import io.cassandrareaper.service.RingRange;

import java.math.BigInteger;
import java.util.UUID;

import javax.annotation.Nullable;

import com.google.common.base.Preconditions;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class RepairSegment {

  private static final Logger LOG = LoggerFactory.getLogger(RepairSegment.class);

  private final UUID id;
  private final UUID runId;
  private final UUID repairUnitId;
  private final RingRange tokenRange;
  private final int failCount;
  private final State state;
  private final String coordinatorHost;
  private final DateTime startTime;
  private final DateTime endTime;

  private RepairSegment(Builder builder, @Nullable UUID id) {
    this.id = id;
    this.runId = builder.runId;
    this.repairUnitId = builder.repairUnitId;
    this.tokenRange = builder.tokenRange;
    this.failCount = builder.failCount;
    this.state = builder.state;
    this.coordinatorHost = builder.coordinatorHost;
    this.startTime = builder.startTime;
    this.endTime = builder.endTime;
  }

  public static Builder builder(RingRange tokenRange, UUID repairUnitId) {
    return new Builder(tokenRange, repairUnitId);
  }

  public UUID getId() {
    return id;
  }

  public UUID getRunId() {
    return runId;
  }

  public UUID getRepairUnitId() {
    return repairUnitId;
  }

  public RingRange getTokenRange() {
    return tokenRange;
  }

  public BigInteger getStartToken() {
    return tokenRange.getStart();
  }

  public BigInteger getEndToken() {
    return tokenRange.getEnd();
  }

  public int getFailCount() {
    return failCount;
  }

  public RepairSegment.State getState() {
    return state;
  }

  @Nullable
  public String getCoordinatorHost() {
    return coordinatorHost;
  }

  @Nullable
  public DateTime getStartTime() {
    return startTime;
  }

  @Nullable
  public DateTime getEndTime() {
    return endTime;
  }

  public Builder with() {
    return new Builder(this);
  }

  public enum State {
    NOT_STARTED,
    RUNNING,
    DONE
  }

  public static final class Builder {

    public final RingRange tokenRange;
    private final UUID repairUnitId;
    private UUID runId;
    private int failCount;
    private State state;
    private String coordinatorHost;
    private DateTime startTime;
    private DateTime endTime;

    private Builder(RingRange tokenRange, UUID repairUnitId) {
      Preconditions.checkNotNull(tokenRange);
      Preconditions.checkNotNull(repairUnitId);
      this.repairUnitId = repairUnitId;
      this.tokenRange = tokenRange;
      this.failCount = 0;
      this.state = State.NOT_STARTED;
    }

    private Builder(RepairSegment original) {
      runId = original.runId;
      repairUnitId = original.repairUnitId;
      tokenRange = original.tokenRange;
      failCount = original.failCount;
      state = original.state;
      coordinatorHost = original.coordinatorHost;
      startTime = original.startTime;
      endTime = original.endTime;
    }

    public Builder withRunId(UUID runId) {
      Preconditions.checkNotNull(runId);
      this.runId = runId;
      return this;
    }

    public Builder failCount(int failCount) {
      this.failCount = failCount;
      return this;
    }

    public Builder state(State state) {
      Preconditions.checkNotNull(state);
      this.state = state;
      return this;
    }

    public Builder coordinatorHost(@Nullable String coordinatorHost) {
      this.coordinatorHost = coordinatorHost;
      return this;
    }

    public Builder startTime(@Nullable DateTime startTime) {
      Preconditions.checkState(
          null != startTime || null == endTime,
          "unsetting startTime only permitted if endTime unset");

      this.startTime = startTime;
      return this;
    }

    public Builder endTime(DateTime endTime) {
      Preconditions.checkNotNull(endTime);
      this.endTime = endTime;
      return this;
    }

    public RepairSegment build(@Nullable UUID segmentId) {
      // a null segmentId is a special case where the storage uses a sequence for it
      //Preconditions.checkNotNull(runId);
      //Preconditions.checkState(null != startTime || null == endTime, "if endTime is set, so must startTime be set");
      //Preconditions.checkState(null == endTime || State.DONE == state, "endTime can only be set if segment is DONE");
      if (null == startTime && null != endTime) {
        LOG.warn(
            "endTime is set but startTime is not on segment {} for repair {}. This should not be happening :(",
            segmentId,
            runId);
      }

      if (null != endTime && State.DONE != state) {
        LOG.warn(
            "endTime is set but segment state is {} is not on segment {} for repair {}. "
                + "This should not be happening :(",
            state,
            segmentId,
            runId);
      }

      if (null != endTime && State.DONE != state) {
        LOG.warn(
            "endTime is set but segment state is {} is not on segment {} for repair {}. "
                + "This should not be happening :(",
            state,
            segmentId,
            runId);
      }

      /* Preconditions.checkState(
      null != startTime || State.NOT_STARTED == state,
      "startTime can only be unset if segment is NOT_STARTED"); */

      if (null == startTime && State.NOT_STARTED != state) {
        LOG.warn(
            "startTime can only be unset if segment state is {} (currently = {}) on segment {} "
                + "for repair {}. This should not be happening :(",
            State.NOT_STARTED,
            state,
            segmentId,
            runId);
      }

      return new RepairSegment(this, segmentId);
    }
  }
}

@@ -688,17 +688,16 @@
 l.UUID;%0A
-%0A
 import j
@@ -803,183 +803,43 @@
 me;%0A
-import org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;%0A%0Apublic final class RepairSegment %7B%0A%0A  private static final Logger LOG = LoggerFactory.getLogger(RepairSegment.class);
+%0Apublic final class RepairSegment %7B
 %0A%0A  
@@ -4547,26 +4547,24 @@
 or it%0A      
-//
 Precondition
@@ -4584,34 +4584,32 @@
 l(runId);%0A      
-//
 Preconditions.ch
@@ -4709,18 +4709,16 @@
 ;%0A      
-//
 Precondi
@@ -4825,816 +4825,14 @@
 %22);%0A
+%0A
      
- if (null == startTime && null != endTime) %7B%0A        LOG.warn(%0A            %22endTime is set but startTime is not on segment %7B%7D for repair %7B%7D. This should not be happening :(%22,%0A            segmentId,%0A            runId);%0A      %7D%0A%0A      if (null != endTime && State.DONE != state) %7B%0A        LOG.warn(%0A            %22endTime is set but segment state is %7B%7D is not on segment %7B%7D for repair %7B%7D. %22%0A                + %22This should not be happening :(%22,%0A            state,%0A            segmentId,%0A            runId);%0A      %7D%0A%0A      if (null != endTime && State.DONE != state) %7B%0A        LOG.warn(%0A            %22endTime is set but segment state is %7B%7D is not on segment %7B%7D for repair %7B%7D. %22%0A                + %22This should not be happening :(%22,%0A            state,%0A            segmentId,%0A            runId);%0A      %7D%0A%0A      /*
  Pre
@@ -4850,24 +4850,28 @@
 checkState(%0A
+    
       null !
@@ -4915,24 +4915,28 @@
 tate,%0A      
+    
 %22startTime c
@@ -4984,357 +4984,8 @@
 D%22);
- */%0A%0A      if (null == startTime && State.NOT_STARTED != state) %7B%0A        LOG.warn(%0A            %22startTime can only be unset if segment state is %7B%7D (currently = %7B%7D) on segment %7B%7D %22%0A                + %22for repair %7B%7D. This should not be happening :(%22,%0A            State.NOT_STARTED,%0A            state,%0A            segmentId,%0A            runId);%0A      %7D
 %0A%0A  

Revert "Remove precondition checks that break Reaper when endTime/startTime are inconsistently set"