/*
 * 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
 *
 *     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 org.apache.paimon.spark;

import org.apache.paimon.options.ConfigOption;

import static org.apache.paimon.options.ConfigOptions.key;

/** Options for spark connector. */
public class SparkConnectorOptions {

    public static final ConfigOption<Boolean> REQUIRED_SPARK_CONFS_CHECK_ENABLED =
            key("requiredSparkConfsCheck.enabled")
                    .booleanType()
                    .defaultValue(true)
                    .withDescription(
                            "Whether to verify SparkSession is initialized with required configurations.");

    public static final ConfigOption<Boolean> LEGACY_TIMESTAMP_MAPPING =
            key("legacy-timestamp-mapping.enabled")
                    .booleanType()
                    .defaultValue(false)
                    .withDescription(
                            "If true, map Paimon TIMESTAMP to Spark TIMESTAMP instead of TIMESTAMP_NTZ.");

    public static final ConfigOption<Integer> VECTOR_SEARCH_LATERAL_JOIN_PARALLELISM =
            key("vector-search.lateral-join.parallelism")
                    .intType()
                    .defaultValue(16)
                    .withDescription(
                            "Parallelism used to repartition a single-partition LIMIT input before "
                                    + "executing a lateral vector search.");

    public static final ConfigOption<Boolean> MERGE_SCHEMA =
            key("write.merge-schema")
                    .booleanType()
                    .defaultValue(false)
                    .withDescription(
                            "If true, evolve the table schema to accept new columns from the incoming data. "
                                    + "Existing column types are preserved and incoming values are cast to them; "
                                    + "to also widen existing types, enable 'write.merge-schema.type-widening'.");

    public static final ConfigOption<Boolean> TYPE_WIDENING =
            key("write.merge-schema.type-widening")
                    .booleanType()
                    .defaultValue(false)
                    .withDescription(
                            "Only effective when 'write.merge-schema' is true. "
                                    + "If true, widen an existing column type when the incoming data has a wider "
                                    + "compatible type (e.g. INT -> BIGINT, DECIMAL precision increase). "
                                    + "Lossy changes are still rejected unless 'write.merge-schema.explicit-cast' is also true.");

    public static final ConfigOption<Boolean> EXPLICIT_CAST =
            key("write.merge-schema.explicit-cast")
                    .booleanType()
                    .defaultValue(false)
                    .withDescription(
                            "Only effective when 'write.merge-schema.type-widening' is true. "
                                    + "If true, also allow lossy type changes between compatible types "
                                    + "(e.g. BIGINT -> INT, STRING -> DATE).");

    public static final ConfigOption<Boolean> USE_V2_WRITE =
            key("write.use-v2-write")
                    .booleanType()
                    .defaultValue(false)
                    .withDescription(
                            "If true, v2 write will be used. Currently, only HASH_FIXED and BUCKET_UNAWARE bucket modes are supported. Will fall back to v1 write for other bucket modes. Currently, Spark V2 write does not support TableCapability.STREAMING_WRITE.");

    public static final ConfigOption<Integer> DATA_EVOLUTION_UPDATE_CONFLICT_RETRY_MAX_ATTEMPTS =
            key("write.data-evolution.update-conflict-retry.max-attempts")
                    .intType()
                    .defaultValue(20)
                    .withDescription(
                            "Maximum attempts for Spark V1 UPDATE on data-evolution tables when "
                                    + "concurrent partial-column updates conflict on the same row-id "
                                    + "range and update columns. Values less than 2 disable retry.");

    public static final ConfigOption<Long> DATA_EVOLUTION_UPDATE_CONFLICT_RETRY_WAIT_MS =
            key("write.data-evolution.update-conflict-retry.wait-ms")
                    .longType()
                    .defaultValue(10L)
                    .withDescription(
                            "Wait time in milliseconds between retry attempts for Spark V1 UPDATE "
                                    + "on data-evolution tables after row-id range update conflicts.");

    public static final ConfigOption<Integer> MAX_FILES_PER_TRIGGER =
            key("read.stream.maxFilesPerTrigger")
                    .intType()
                    .noDefaultValue()
                    .withDescription("The maximum number of files returned in a single batch.");

    public static final ConfigOption<Long> MAX_BYTES_PER_TRIGGER =
            key("read.stream.maxBytesPerTrigger")
                    .longType()
                    .noDefaultValue()
                    .withDescription("The maximum number of bytes returned in a single batch.");

    public static final ConfigOption<Long> MAX_ROWS_PER_TRIGGER =
            key("read.stream.maxRowsPerTrigger")
                    .longType()
                    .noDefaultValue()
                    .withDescription("The maximum number of rows returned in a single batch.");

    public static final ConfigOption<Long> MIN_ROWS_PER_TRIGGER =
            key("read.stream.minRowsPerTrigger")
                    .longType()
                    .noDefaultValue()
                    .withDescription(
                            "The minimum number of rows returned in a single batch, which used to create MinRowsReadLimit with read.stream.maxTriggerDelayMs together.");

    public static final ConfigOption<Long> MAX_DELAY_MS_PER_TRIGGER =
            key("read.stream.maxTriggerDelayMs")
                    .longType()
                    .noDefaultValue()
                    .withDescription(
                            "The maximum delay between two adjacent batches, which used to create MinRowsReadLimit with read.stream.minRowsPerTrigger together.");

    public static final ConfigOption<Boolean> READ_CHANGELOG =
            key("read.changelog")
                    .booleanType()
                    .defaultValue(false)
                    .withDescription(
                            "Whether to read row in the form of changelog (add rowkind column in row to represent its change type).");

    public static final ConfigOption<Boolean> READ_ALLOW_FULL_SCAN =
            key("read.allow.fullScan")
                    .booleanType()
                    .defaultValue(true)
                    .withDescription(
                            "Whether to allow full scan when reading a partitioned table.");

    public static final ConfigOption<Boolean> SOURCE_SPLIT_TARGET_SIZE_WITH_COLUMN_PRUNING =
            key("source.split.target-size-with-column-pruning")
                    .booleanType()
                    .defaultValue(false)
                    .withDescription(
                            "Whether to adjust the target split size based on pruned (projected) columns. "
                                    + "If enabled, split size estimation uses only the columns actually being read.");
}
