/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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
 *
 *   https://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 org.apache.accumulo.server.util;

import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.accumulo.core.client.AccumuloClient;
import org.apache.accumulo.core.client.IteratorSetting;
import org.apache.accumulo.core.client.IteratorSetting.Column;
import org.apache.accumulo.core.client.admin.TableOperations;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.core.clientImpl.ClientContext;
import org.apache.accumulo.core.clientImpl.Credentials;
import org.apache.accumulo.core.clientImpl.Writer;
import org.apache.accumulo.core.conf.Property;
import org.apache.accumulo.core.data.ColumnUpdate;
import org.apache.accumulo.core.data.Mutation;
import org.apache.accumulo.core.data.TableId;
import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.dataImpl.KeyExtent;
import org.apache.accumulo.core.iterators.Combiner;
import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
import org.apache.accumulo.core.metadata.schema.MetadataSchema.ReplicationSection;
import org.apache.accumulo.core.protobuf.ProtobufUtil;
import org.apache.accumulo.server.replication.StatusCombiner;
import org.apache.accumulo.server.replication.StatusUtil;
import org.apache.accumulo.server.replication.proto.Replication.Status;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.easymock.EasyMock;
import org.junit.jupiter.api.Test;

@Deprecated
public class ReplicationTableUtilTest {

  @Test
  public void properPathInRow() throws Exception {
    Writer writer = EasyMock.createNiceMock(Writer.class);
    writer.update(EasyMock.anyObject(Mutation.class));
    final List<Mutation> mutations = new ArrayList<>();

    // Mock a Writer to just add the mutation to a list
    EasyMock.expectLastCall().andAnswer(() -> {
      mutations.add(((Mutation) EasyMock.getCurrentArguments()[0]));
      return null;
    });

    EasyMock.replay(writer);

    Credentials creds = new Credentials("root", new PasswordToken(""));
    ClientContext context = EasyMock.createMock(ClientContext.class);
    EasyMock.expect(context.getCredentials()).andReturn(creds).anyTimes();
    EasyMock.replay(context);

    // Magic hook to create a Writer
    ReplicationTableUtil.addWriter(creds, writer);

    // Example file seen coming out of LogEntry
    UUID uuid = UUID.randomUUID();
    String myFile = "file:////home/user/accumulo/wal/server+port/" + uuid;

    long createdTime = System.currentTimeMillis();
    ReplicationTableUtil.updateFiles(context, new KeyExtent(TableId.of("1"), null, null), myFile,
        StatusUtil.fileCreated(createdTime));

    verify(writer);

    assertEquals(1, mutations.size());
    Mutation m = mutations.get(0);

    assertEquals(
        ReplicationSection.getRowPrefix() + "file:/home/user/accumulo/wal/server+port/" + uuid,
        new Text(m.getRow()).toString());

    List<ColumnUpdate> updates = m.getUpdates();
    assertEquals(1, updates.size());
    ColumnUpdate update = updates.get(0);

    assertEquals(ReplicationSection.COLF, new Text(update.getColumnFamily()));
    assertEquals("1", new Text(update.getColumnQualifier()).toString());
    assertEquals(StatusUtil.fileCreatedValue(createdTime), new Value(update.getValue()));
  }

  @Test
  public void replEntryMutation() {
    // We stopped using a WAL -- we need a reference that this WAL needs to be replicated completely
    Status stat = Status.newBuilder().setBegin(0).setEnd(0).setInfiniteEnd(true)
        .setCreatedTime(System.currentTimeMillis()).build();
    String file = "file:///accumulo/wal/127.0.0.1+9997" + UUID.randomUUID();
    Path filePath = new Path(file);
    Text row = new Text(filePath.toString());
    KeyExtent extent = new KeyExtent(TableId.of("1"), new Text("b"), new Text("a"));

    Mutation m =
        ReplicationTableUtil.createUpdateMutation(filePath, ProtobufUtil.toValue(stat), extent);

    assertEquals(new Text(ReplicationSection.getRowPrefix() + row), new Text(m.getRow()));
    assertEquals(1, m.getUpdates().size());
    ColumnUpdate col = m.getUpdates().get(0);

    assertEquals(ReplicationSection.COLF, new Text(col.getColumnFamily()));
    assertEquals(extent.tableId().canonical(), new Text(col.getColumnQualifier()).toString());
    assertEquals(0, col.getColumnVisibility().length);
    assertArrayEquals(stat.toByteArray(), col.getValue());
  }

  @Test
  public void setsCombinerOnMetadataCorrectly() throws Exception {
    AccumuloClient client = createMock(AccumuloClient.class);
    TableOperations tops = createMock(TableOperations.class);

    String myMetadataTable = "mymetadata";
    Map<String,EnumSet<IteratorScope>> iterators = new HashMap<>();
    iterators.put("vers", EnumSet.of(IteratorScope.majc, IteratorScope.minc, IteratorScope.scan));
    IteratorSetting combiner = new IteratorSetting(9, "replcombiner", StatusCombiner.class);
    Combiner.setColumns(combiner, Collections.singletonList(new Column(ReplicationSection.COLF)));

    expect(client.tableOperations()).andReturn(tops);
    expect(tops.listIterators(myMetadataTable)).andReturn(iterators);
    tops.attachIterator(myMetadataTable, combiner);
    expectLastCall().once();

    expect(tops.getConfiguration(myMetadataTable)).andReturn(Collections.emptyMap());
    tops.setProperty(myMetadataTable, Property.TABLE_FORMATTER_CLASS.getKey(),
        ReplicationTableUtil.STATUS_FORMATTER_CLASS_NAME);
    expectLastCall().once();

    replay(client, tops);

    ReplicationTableUtil.configureMetadataTable(client, myMetadataTable);

    verify(client, tops);
  }
}
