/*
 * Copyright 2002-2025 the original author or authors.
 *
 * 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
 *
 *      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.springframework.batch.extensions.bigquery.writer;

import com.google.cloud.bigquery.FormatOptions;
import com.google.cloud.bigquery.Table;
import org.springframework.batch.item.json.JsonObjectMarshaller;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;

/**
 * JSON writer for BigQuery.
 *
 * @param <T> your DTO type
 * @author Volodymyr Perebykivskyi
 * @since 0.2.0
 * @see <a href="https://en.wikipedia.org/wiki/JSON">JSON</a>
 */
public class BigQueryJsonItemWriter<T> extends BigQueryBaseItemWriter<T> {

    private static final String LF = "\n";

    private JsonObjectMarshaller<T> marshaller;

    @Override
    protected void doInitializeProperties(List<? extends T> items) {
        // Unused
    }

    @Override
    protected List<byte[]> convertObjectsToByteArrays(List<? extends T> items) {
        return items
                .stream()
                .map(marshaller::marshal)
                .filter(Predicate.not(ObjectUtils::isEmpty))
                .map(this::convertToNdJson)
                .map(row -> row.getBytes(StandardCharsets.UTF_8))
                .toList();
    }

    @Override
    protected void performFormatSpecificChecks() {
        Assert.notNull(this.marshaller, "Marshaller is mandatory");

        Table table = getTable();

        if (Boolean.TRUE.equals(writeChannelConfig.getAutodetect())) {
            if (tableHasDefinedSchema(table) && super.logger.isWarnEnabled()) {
                logger.warn("Mixing autodetect mode with already defined schema may lead to errors on BigQuery side");
            }
        } else {
            Assert.notNull(writeChannelConfig.getSchema(), "Schema must be provided");

            if (tableHasDefinedSchema(table)) {
                Assert.isTrue(
                        Objects.equals(table.getDefinition().getSchema(), writeChannelConfig.getSchema()),
                        "Schema must be the same"
                );
            }
        }

        String format = FormatOptions.json().getType();
        Assert.isTrue(Objects.equals(format, super.writeChannelConfig.getFormat()), "Only %s format is allowed".formatted(format));
    }

    /**
     * Converter that transforms a single row into a {@link String}.
     *
     * @param marshaller your JSON mapper
     */
    public void setMarshaller(JsonObjectMarshaller<T> marshaller) {
        this.marshaller = marshaller;
    }

    /**
     * BigQuery uses <a href="https://github.com/ndjson/ndjson-spec">ndjson</a>.
     * It is expected that to pass here JSON line generated by
     * {@link com.fasterxml.jackson.databind.ObjectMapper} or any other JSON parser.
     */
    private String convertToNdJson(String json) {
        return json.concat(LF);
    }

}