/*
 * 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.avro;

import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericDatumWriter;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.BinaryDecoder;
import org.apache.avro.io.BinaryEncoder;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DecoderFactory;
import org.apache.avro.io.EncoderFactory;

import org.junit.jupiter.api.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class TestUnionError {

  @Test
  void unionErrorMessage() throws IOException {
    String writerSchemaJson = "            {\n" + "              \"type\" : \"record\",\n"
        + "              \"name\" : \"C\",\n" + "              \"fields\" : [ {\n"
        + "                \"name\" : \"c\",\n" + "                \"type\" : [ {\n"
        + "                  \"type\" : \"record\",\n" + "                  \"name\" : \"A\",\n"
        + "                  \"fields\" : [ {\n" + "                    \"name\" : \"amount\",\n"
        + "                    \"type\" : \"int\"\n" + "                  } ]\n" + "                }, {\n"
        + "                  \"type\" : \"record\",\n" + "                  \"name\" : \"B\",\n"
        + "                  \"fields\" : [ {\n" + "                    \"name\" : \"amount1\",\n"
        + "                    \"type\" : \"int\"\n" + "                  } ]\n" + "                } ]\n"
        + "              } ]\n" + "            }";
    Schema writerSchema = SchemaParser.parseSingle(writerSchemaJson);

    String readerSchemaJson = " {\n" + "              \"type\" : \"record\",\n" + "              \"name\" : \"C1\",\n"
        + "              \"fields\" : [ {\n" + "                \"name\" : \"c\",\n"
        + "                \"type\" : [ {\n" + "                  \"type\" : \"record\",\n"
        + "                  \"name\" : \"A\",\n" + "                  \"fields\" : [ {\n"
        + "                    \"name\" : \"amount\",\n" + "                    \"type\" : \"int\"\n"
        + "                  } ]\n" + "                }, \"float\" ]\n" + "              } ]\n" + "            }";
    Schema readerSchema = SchemaParser.parseSingle(readerSchemaJson);

    List<Schema> unionSchemas = writerSchema.getField("c").schema().getTypes();

    GenericRecord r = new GenericData.Record(writerSchema);
    GenericRecord b = new GenericData.Record(unionSchemas.get(1));
    b.put("amount1", 12);
    r.put("c", b);

    ByteArrayOutputStream outs = new ByteArrayOutputStream();
    GenericDatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<>(writerSchema);
    BinaryEncoder encoder = EncoderFactory.get().binaryEncoder(outs, null);
    datumWriter.write(r, encoder);
    encoder.flush();

    InputStream ins = new ByteArrayInputStream(outs.toByteArray());
    BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(ins, null);

    GenericData data = new GenericData();
    data.setFastReaderEnabled(false);
    GenericDatumReader<GenericRecord> datumReader = new GenericDatumReader<>(writerSchema, readerSchema, data);
    AvroTypeException avroException = assertThrows(AvroTypeException.class, () -> datumReader.read(null, decoder));
    assertEquals("Field \"c\" content mismatch: Found B, expecting union[A, float]", avroException.getMessage());
  }
}
