package com.fshark.overseas.advert.app;


import com.fshark.overseas.advert.modle.AppsFlyerEvent;
import com.fshark.overseas.advert.modle.EventTypeEnum;
import com.fshark.overseas.advert.modle.SinkMetricEvent;
import com.fshark.overseas.advert.modle.metrics.ActivationMetrics;
import com.fshark.overseas.advert.processor.*;
import com.fshark.overseas.advert.util.AppConfig;
import com.fshark.overseas.advert.util.Constants;
import com.google.gson.Gson;
import org.apache.flink.api.common.functions.FilterFunction;
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.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.ProcessFunction;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer;
import org.apache.flink.util.Collector;
import org.apache.flink.util.OutputTag;

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

/**
 * 广告计算
 */
public class AppsflyerAnalyze {
    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.setStartOffsetTime(Long.valueOf(parameters.get("start-offset-time")));

            appConfig.setSinkKafkaBootstrapServers(parameters.get("sink-kafka-bootstrap-servers"));
            appConfig.setSinkKafkaTopic(parameters.get("sink-kafka-topic"));

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

        } 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>" +
                    " --sink-kafka-bootstrap-servers <sink-kafka-bootstrap-servers>" +
                    " --sink-kafka-group-id <sink-kafka-group-id>" +
                    " --sink-kafka-topic <sink-kafka-topic>" +
                    " --jdbc-host <jdbc-host> " +
                    " --jdbc-user <jdbc-user> " +
                    " --jdbc-password <jdbc-password> ");
            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);
        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", "earliest");
        FlinkKafkaConsumer<String> dataConsumer = new FlinkKafkaConsumer<>(appConfig.getSourceKafkaTopic(), new SimpleStringSchema(), properties);

        Properties props2 = new Properties();
        props2.setProperty("bootstrap.servers", appConfig.getSinkKafkaBootstrapServers());
        FlinkKafkaProducer<String> kafkaSink = new FlinkKafkaProducer<>(appConfig.getSinkKafkaTopic(), new SimpleStringSchema(), props2);

        DataStream<AppsFlyerEvent> adEventDataStream = sse.addSource(dataConsumer)
                .flatMap(new ParseEventProcessor())
                .filter(new FilterFunction<AppsFlyerEvent>() {
                    @Override
                    public boolean filter(AppsFlyerEvent value) throws Exception {
                        if (value.getMedia_source().contains("rank") || value.getCampaign_id().contains("rank")) {
                            return false;
                        }
                        return true;
                    }
                }).process(new MutiplatDataProcess())
                .uid("parse-event")
                .name("parse-event");


        //测流提取出来激活，登录，创角角色登录,订单事件
        OutputTag<AppsFlyerEvent> installTag = new OutputTag<AppsFlyerEvent>("install") {
        };
        OutputTag<AppsFlyerEvent> loginTag = new OutputTag<AppsFlyerEvent>("login") {
        };
        OutputTag<AppsFlyerEvent> roleLoginTag = new OutputTag<AppsFlyerEvent>("role_login") {
        };
        OutputTag<AppsFlyerEvent> orderTag = new OutputTag<AppsFlyerEvent>("order") {
        };
        OutputTag<AppsFlyerEvent> sdkRegisterTag = new OutputTag<AppsFlyerEvent>("sdk_register") {
        };
        SingleOutputStreamOperator<AppsFlyerEvent> mainStream = adEventDataStream.process(new ProcessFunction<AppsFlyerEvent, AppsFlyerEvent>() {
            @Override
            public void processElement(AppsFlyerEvent value, Context ctx, Collector<AppsFlyerEvent> out) throws Exception {
                if (EventTypeEnum.installEvent(value)) {
                    ctx.output(installTag, value);
                } else if (EventTypeEnum.loginEvent(value)) {
                    ctx.output(loginTag, value);
                } else if (EventTypeEnum.roleEvent(value)) {
                    ctx.output(roleLoginTag, value);
                } else if (EventTypeEnum.orderEvent(value)) {
                    ctx.output(orderTag, value);
                } else if (EventTypeEnum.sdkEvent(value)) {
                    ctx.output(sdkRegisterTag, value);
                } else {
                    out.collect(value);
                }
            }
        })
                .uid("shuffle-event")
                .name("shuffle-event");

        DataStream<AppsFlyerEvent> installStream = mainStream.getSideOutput(installTag);
        DataStream<AppsFlyerEvent> loginStream = mainStream.getSideOutput(loginTag);
        DataStream<AppsFlyerEvent> roleloginStream = mainStream.getSideOutput(roleLoginTag);
        DataStream<AppsFlyerEvent> orderStream = mainStream.getSideOutput(orderTag);
        DataStream<AppsFlyerEvent> sdkRegisterStream = mainStream.getSideOutput(sdkRegisterTag);

        //广告指标(激活)
        DataStream<ActivationMetrics> activitionMetricStream = installStream
                .flatMap(new ActivitionProcess(appConfig))
                .uid("activition-event")
                .name("activition-event");

        //注册\留存 由于可能出现创角和角色登录是一瞬间的,所以他两合并计算
        DataStream<ActivationMetrics> loginMetricStream = loginStream.union(roleloginStream)
                .flatMap(new RoleLoginProcess(appConfig))
                .uid("role-login-event")
                .name("role-login-event");

        //sdk激活注册 当天
        DataStream<ActivationMetrics> sdkMetrics = sdkRegisterStream
                .flatMap(new SdkProcess(appConfig))
                .uid("sdk-event")
                .name("sdk-event");

        activitionMetricStream.union(loginMetricStream).union(sdkMetrics)
                .flatMap(new ActivitionMetricToStringProcess(appConfig))
                .addSink(kafkaSink)
                .uid("sink-metric-event")
                .name("sink-metric-event");

        /**
         * ouid相关指标计算 注册留存日活 新增创角指标
         */
        DataStream<ActivationMetrics> ouidMetrics = loginStream.union(roleloginStream)
                .flatMap(new OuidLoginProcess(appConfig))
                .uid("ouid-event")
                .name("ouid-event");


        ouidMetrics.flatMap(new RichFlatMapFunction<ActivationMetrics, String>() {

                    @Override
                    public void flatMap(ActivationMetrics value, Collector<String> out) throws Exception {
                        SinkMetricEvent sinkMetricEvent = new SinkMetricEvent();
                        sinkMetricEvent.setEventData(value);
                        sinkMetricEvent.setEventType(Constants.TYPE_SINK_OUID_METRIC);
                        out.collect(new Gson().toJson(sinkMetricEvent));
                    }
                })
                .addSink(kafkaSink)
                .uid("sink_ouid-event")
                .name("sink_ouid-event");

//      af订单信息
        orderStream
                .flatMap(new PurchaseToOrderDataProcessor(appConfig))
                .addSink(kafkaSink)
                .uid("order-event")
                .name("order-event");

        /**
         * ouid 的订单信息
         */
        orderStream.flatMap(new OuidOrderDataProcessor(appConfig))
                .addSink(kafkaSink)
                .uid("ouid_order-metrics")
                .name("ouid_order-metrics");

        //事件统计激活
        mainStream
                .filter(EventTypeEnum::notHeartBeatEvent)
                .keyBy(AppsFlyerEvent::getUid)
                .flatMap(new EventMectrisProcessor(appConfig))
                .flatMap(new EventMetricsToStringProcessor())
                .addSink(kafkaSink)
                .uid("aggregate-event")
                .name("aggregate-event");

        //
        sse.execute("appsflyer-analyze");

    }
}
