package com.fshark.overseas.advert;

import com.fshark.overseas.advert.modle.SinkMetricEvent;
import com.fshark.overseas.advert.modle.metrics.ActivationMetrics;
import com.fshark.overseas.advert.modle.metrics.RemainMetrics;
import com.fshark.overseas.advert.modle.metrics.ServerActivationMetrics;
import com.fshark.overseas.advert.modle.metrics.ServerRemainMetrics;
import com.fshark.overseas.advert.processor.*;
import com.fshark.overseas.advert.processor.server.ServerActivationAggregateFunction;
import com.fshark.overseas.advert.processor.server.ServerActivationResultWindows;
import com.fshark.overseas.advert.processor.server.ServerRemainProcessor;
import com.fshark.overseas.advert.sink.ActicitionSink;
import com.fshark.overseas.advert.sink.EventTimesSink;
import com.fshark.overseas.advert.sink.PurchaseSink;
import com.fshark.overseas.advert.sink.RemainSink;
import com.fshark.overseas.advert.sink.server.ServerActivationSink;
import com.fshark.overseas.advert.sink.server.ServerRemindSink;
import com.fshark.overseas.advert.util.AppConfig;
import com.google.gson.Gson;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.restartstrategy.RestartStrategies;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.java.utils.ParameterTool;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;
import org.apache.flink.util.Collector;

import java.util.Properties;
import java.util.concurrent.TimeUnit;

public class AppsflyerMetricStore {
    static AppConfig loadConfigFromArgs(String[] args) {
        ParameterTool parameters = ParameterTool.fromArgs(args);
        AppConfig appConfig = new AppConfig();
        try {
            appConfig.setSourceKafkaBootstrapServers(parameters.get("source-kafka-bootstrap-servers"));
            appConfig.setSourceKafkaGroupId(parameters.get("source-kafka-group-id"));
            appConfig.setSourceKafkaTopic(parameters.get("source-kafka-topic"));

            appConfig.setDbHost(parameters.get("jdbc-host"));
            appConfig.setDbUser(parameters.get("jdbc-user"));
            appConfig.setDbPassword(parameters.get("jdbc-password"));

            appConfig.setSinkType(parameters.get("sink-type"));

        } catch (Exception e) {
            System.err.println("Place set args: " +
                    " --source-kafka-bootstrap-servers <source-kafka-bootstrap-servers>" +
                    " --source-kafka-group-id <source-kafka-group-id>" +
                    " --source-kafka-topic <source-kafka-topic>" +
                    " --jdbc-host <jdbc-host> " +
                    " --jdbc-user <jdbc-user> " +
                    " --jdbc-password <jdbc-password> " +
                    " --sink-type <sink-type>");
            throw e;
        }
        return appConfig;
    }

    public static void main(String[] args) throws Exception {
        AppConfig appConfig = loadConfigFromArgs(args);
        final StreamExecutionEnvironment sse = StreamExecutionEnvironment.getExecutionEnvironment();
        sse.setParallelism(1);
        sse.enableCheckpointing(1000 * 60 * 10);
        sse.setRestartStrategy(RestartStrategies.fixedDelayRestart(Integer.MAX_VALUE, org.apache.flink.api.common.time.Time.of(10, TimeUnit.SECONDS)));

        //source设置
        Properties properties = new Properties();
        properties.setProperty("bootstrap.servers", appConfig.getSourceKafkaBootstrapServers());
        properties.setProperty("group.id", appConfig.getSourceKafkaGroupId());
        properties.setProperty("auto.offset.reset", "latest");
        FlinkKafkaConsumer<String> dataConsumer = new FlinkKafkaConsumer<>(appConfig.getSourceKafkaTopic(), new SimpleStringSchema(), properties);

        DataStream<SinkMetricEvent> activationMetricsDataStream = sse.addSource(dataConsumer)
                .flatMap(new RichFlatMapFunction<String, SinkMetricEvent>() {
                    @Override
                    public void flatMap(String value, Collector<SinkMetricEvent> out) throws Exception {
                        out.collect(new Gson().fromJson(value, SinkMetricEvent.class));
                    }
                })
                .uid("parse-event")
                .name("parse-event");

//        activationMetricsDataStream.print();
        //角色注册激活预聚合
        DataStream<ActivationMetrics> activitionResult = activationMetricsDataStream
                .flatMap(new MetricJsonToObProcessor())
                .keyBy(ActivationMetrics::getKey)
                .window(TumblingProcessingTimeWindows.of(Time.minutes(1)))
                .aggregate(new ActivationAggregateFunction(), new ActivationResultWindows())
                .uid("activation-agg-event")
                .name("activation-agg-event");

        //      角色注册激活写入
        activitionResult.addSink(new ActicitionSink(appConfig))
                .uid("login-sink-event")
                .name("login-sink-event");
//
//        //指标详细记录
        activationMetricsDataStream.timeWindowAll(Time.minutes(1)).process(new StoreMetricProcess(appConfig))
                .uid("metric-detail")
                .name("metric-detail");

//        留存预聚合
        DataStream<RemainMetrics> remainResult = activationMetricsDataStream.flatMap(new RemainProcess())
                .keyBy(RemainMetrics::getRemind_key)
                .window(TumblingProcessingTimeWindows.of(Time.minutes(1)))
                .sum("user_num")
                .uid("remain-event")
                .name("remain-event");
        //留存写入
        remainResult
                .addSink(new RemainSink(appConfig))
                .uid("remain-sink-event")
                .name("remain-sink-event");

        //订单写入
        activationMetricsDataStream
                .filter(SinkMetricEvent::isOrderEvent)
                .addSink(new PurchaseSink(appConfig))
                .uid("order-sink-event")
                .name("order-sink-event");


        //角色注册激活预聚合
        DataStream<ServerActivationMetrics> activationMetricsList = activationMetricsDataStream
                .flatMap(new RichFlatMapFunction<SinkMetricEvent, ServerActivationMetrics>() {
                    @Override
                    public void flatMap(SinkMetricEvent value, Collector<ServerActivationMetrics> out) {
                        if (value.isServer()) {
                            out.collect(value.getServerActivationMetrics());
                        }
                    }
                })
                .keyBy(ServerActivationMetrics::getKey)
                .window(TumblingProcessingTimeWindows.of(Time.minutes(1)))
                .aggregate(new ServerActivationAggregateFunction(), new ServerActivationResultWindows())
                .uid("server-activation-agg-event")
                .name("server-activation-agg-event");

        activationMetricsList.addSink(new ServerActivationSink(appConfig))
                .uid("server-activationsink-event")
                .name("server-activationsink-event");

        //留存预聚合
        DataStream<ServerRemainMetrics> remainMetrics = activationMetricsDataStream
                .flatMap(new RichFlatMapFunction<SinkMetricEvent, ServerActivationMetrics>() {
                    @Override
                    public void flatMap(SinkMetricEvent value, Collector<ServerActivationMetrics> out) {
                        if (value.isServer()) {
                            out.collect(value.getServerActivationMetrics());
                        }
                    }
                })
                .flatMap(new ServerRemainProcessor(appConfig))
                .keyBy(ServerRemainMetrics::getRemind_key)
                .window(TumblingProcessingTimeWindows.of(Time.minutes(1)))
                .sum("user_num")
                .uid("server-remain-event")
                .name("server-remain-event");
        remainMetrics.addSink(new ServerRemindSink(appConfig))
                .uid("server-remindsink-event")
                .name("server-remindsink-event");

        /**
         * 事件写入
         */
        activationMetricsDataStream.filter(SinkMetricEvent::isEvent)
                .addSink(new EventTimesSink(appConfig))
                .uid("event-sink-event")
                .name("event-sink-event");

        sse.execute("AppsflyerMetricStore-analyze");
    }
}
