/*
 * 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.seatunnel.transform.regular;

import com.google.auto.service.AutoService;
import jdk.nashorn.internal.runtime.JSONFunctions;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.seatunnel.api.configuration.ReadonlyConfig;
import org.apache.seatunnel.api.configuration.util.ConfigValidator;
import org.apache.seatunnel.api.table.catalog.CatalogTable;
import org.apache.seatunnel.api.table.catalog.Column;
import org.apache.seatunnel.api.table.type.BasicType;
import org.apache.seatunnel.api.table.type.SeaTunnelDataType;
import org.apache.seatunnel.api.table.type.SeaTunnelRowType;
import org.apache.seatunnel.api.transform.SeaTunnelTransform;
import org.apache.seatunnel.shade.com.typesafe.config.Config;
import org.apache.seatunnel.transform.common.MultipleFieldOutputTransform;
import org.apache.seatunnel.transform.common.SeaTunnelRowAccessor;

import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@AutoService(SeaTunnelTransform.class)
@NoArgsConstructor
public class ReplaceTransform extends MultipleFieldOutputTransform {

    private ReadonlyConfig config;

    private Map<String,Integer> inputFieldIndex;

    public ReplaceTransform(
            @NonNull ReadonlyConfig config, @NonNull CatalogTable inputCatalogTable) {
        super(inputCatalogTable);
        this.config = config;
        initOutputFields(
                inputCatalogTable.getTableSchema().toPhysicalRowDataType(),
                this.config.get(ReplaceTransformConfig.KEY_REPLACE_FIELD_LIST));
    }

    @Override
    public String getPluginName() {
        return "Replace-Regular";
    }

    @Override
    protected void setConfig(Config pluginConfig) {
        ConfigValidator.of(ReadonlyConfig.fromConfig(pluginConfig))
                .validate(new ReplaceTransformFactory().optionRule());
        this.config = ReadonlyConfig.fromConfig(pluginConfig);
    }

    @Override
    protected void setInputRowType(SeaTunnelRowType inputRowType) {
        initOutputFields(inputRowType, config.get(ReplaceTransformConfig.KEY_REPLACE_FIELD_LIST));
    }

    @Override
    protected String[] getOutputFieldNames() {
        List<ReplaceEntity> list = config.get(ReplaceTransformConfig.KEY_REPLACE_FIELD_LIST);
        List<String> columns = list.stream().map(ReplaceEntity::getColumn).collect(Collectors.toList());
        return (String[]) columns.toArray();
    }

    @Override
    protected SeaTunnelDataType[] getOutputFieldDataTypes() {
        return new BasicType[]{BasicType.STRING_TYPE};
    }

    @Override
    protected Object[] getOutputFieldValues(SeaTunnelRowAccessor inputRow) {

        Object[] objects = new Object[inputFieldIndex.size()];
        List<ReplaceEntity> list = config.get(ReplaceTransformConfig.KEY_REPLACE_FIELD_LIST);
        int i = 0;
        for (ReplaceEntity replaceEntity : list) {
            if (inputFieldIndex.get(replaceEntity.getColumn()) == null || inputFieldIndex.get(replaceEntity.getColumn()) < 0) {
                continue;
            }
            Object inputFieldValue = inputRow.getField(inputFieldIndex.get(replaceEntity.getColumn()));

            if (inputFieldValue == null || inputFieldValue.toString().equals("")) {
                inputFieldValue = "null";
            }
            if (replaceEntity.getIsRegex() != null && replaceEntity.getIsRegex()) {
                regularReplace(objects, i, replaceEntity, inputFieldValue);
            } else {
                replace(objects, i, replaceEntity, inputFieldValue);
            }

            i++;
        }

        return objects;
    }

    /**
     * 正则替换
     */
    private void regularReplace(Object[] objects, int i, ReplaceEntity replaceEntity, Object inputFieldValue) {

        String value = inputFieldValue.toString();
        if (replaceEntity.getReplaceFirst() != null && replaceEntity.getReplaceFirst()) {
            objects[i] = inputFieldValue
                    .toString()
                    .replaceFirst(
                            value,
                            Pattern.matches(replaceEntity.getPattern(), replaceEntity.getPattern()) ? replaceEntity.getReplacement() : value);
        } else {
            objects[i] = inputFieldValue
                    .toString()
                    .replaceAll(
                            value,
                            Pattern.matches(replaceEntity.getPattern(), replaceEntity.getPattern()) ? replaceEntity.getReplacement() : value);
        }
    }

    /**
     * 非正则替换
     */
    private void replace(Object[] objects, int i, ReplaceEntity replaceEntity, Object inputFieldValue) {

        if (replaceEntity.getReplaceFirst() != null && replaceEntity.getReplaceFirst()) {
            objects[i] = inputFieldValue
                    .toString()
                    .replaceFirst(
                            replaceEntity.getPattern(),
                            replaceEntity.getReplacement());
        } else {
            objects[i] = inputFieldValue
                    .toString()
                    .replaceAll(
                            replaceEntity.getPattern(),
                            replaceEntity.getReplacement());
        }
    }

    @Override
    protected Column[] getOutputColumns() {
        Column[] columnList = new Column[inputFieldIndex.size()];
        List<ReplaceEntity> list = config.get(ReplaceTransformConfig.KEY_REPLACE_FIELD_LIST);
        for (int i = 0; i < list.size(); i++) {
            List<Column> columns = inputCatalogTable.getTableSchema().getColumns();
            int finalI = i;
            List<Column> collect =
                    columns.stream().filter(column ->
                                    column.getName().equals(list.get(finalI).getColumn()))
                            .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(collect)) {
                throw new IllegalArgumentException(
                        "Cannot find ["
                                + list.get(finalI).getColumn()
                                + "] field in input catalog table");
            }
            columnList[i] = collect.get(0).copy();
        }

        return columnList;
    }

    private void initOutputFields(SeaTunnelRowType inputRowType, List<ReplaceEntity> replaceFields) {
        inputFieldIndex = new HashMap<>();
        for (ReplaceEntity replaceEntity : replaceFields) {
            if (inputRowType.indexOf(replaceEntity.getColumn()) > 0) {
                inputFieldIndex.put(replaceEntity.getColumn(),inputRowType.indexOf(replaceEntity.getColumn()));
            } else  {
                throw new IllegalArgumentException(
                        "Cannot find [" + replaceEntity.getColumn() + "] field in input row type");
            }
        }
    }
}
