package com.lanyuanxiaoyao.service.api.controller;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.loki4j.slf4j.marker.LabelMarker;
import com.lanyuanxiaoyao.service.api.entity.HoodieCommitMetadata;
import com.lanyuanxiaoyao.service.api.service.SyncStateService;
import com.lanyuanxiaoyao.service.api.service.VersionUpdateService;
import com.lanyuanxiaoyao.service.common.Constants;
import org.eclipse.collections.api.factory.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * Rest 接口
 *
 * @author ZhangJiacheng
 * @date 2022-07-05
 */
@RequestMapping("api")
@RestController
public class ApiController {
    private static final Logger logger = LoggerFactory.getLogger(ApiController.class);

    private final SyncStateService syncStateService;
    private final VersionUpdateService versionUpdateService;
    private final ObjectMapper mapper;

    public ApiController(SyncStateService syncStateService, VersionUpdateService versionUpdateService, Jackson2ObjectMapperBuilder builder) {
        this.syncStateService = syncStateService;
        this.versionUpdateService = versionUpdateService;
        this.mapper = builder.build();
    }

    private Marker makeMarker(Long flinkJobId, String alias) {
        return LabelMarker.of(() -> Maps.mutable.of(Constants.LOG_FLINK_JOB_ID, flinkJobId.toString(), Constants.LOG_ALIAS, alias));
    }

    @ResponseBody
    @GetMapping("message_id")
    public String messageId(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias
    ) {
        logger.info(makeMarker(flinkJobId, alias), "Get message id: {} {}", flinkJobId, alias);
        return syncStateService.getMessageId(flinkJobId, alias);
    }

    @GetMapping("sync_start")
    public void syncStart(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("database") String database,
            @RequestParam("schema") String schema,
            @RequestParam("table") String table,
            @RequestParam("cluster") String cluster,
            @RequestParam("application_id") String applicationId
    ) {
        logger.info(makeMarker(flinkJobId, alias), "Sync start: {} {} {} {}", flinkJobId, alias, cluster, applicationId);
        syncStateService.syncStart(flinkJobId, alias, database, schema, table, cluster, applicationId);
    }

    @GetMapping("sync_checkpoint")
    public void syncCheckpoint(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("message_id") String messageId
    ) {
        logger.info(makeMarker(flinkJobId, alias), "Sync checkpoint: {} {} {}", flinkJobId, alias, messageId);
        syncStateService.syncCheckpoint(flinkJobId, alias, messageId);
    }

    @GetMapping("sync_latest_publish_time")
    public void syncLatestPublishTime(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("publish_time") Long publishTime
    ) {
        logger.info(makeMarker(flinkJobId, alias), "Sync latest publish time: {} {} {}", flinkJobId, alias, publishTime);
        syncStateService.syncPublishTime(flinkJobId, alias, publishTime);
    }

    @GetMapping("sync_latest_receive_time")
    public void syncLatestReceiveTime(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("receive_time") Long receiveTime
    ) {
        logger.info(makeMarker(flinkJobId, alias), "Sync latest receive time: {} {} {}", flinkJobId, alias, receiveTime);
        syncStateService.syncLatestReceiveTime(flinkJobId, alias, receiveTime);
    }

    @GetMapping("sync_latest_operation_time")
    public void syncLatestOperationTime(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("operation_time") Long operationTime
    ) {
        logger.info(makeMarker(flinkJobId, alias), "Sync latest operation time: {} {} {}", flinkJobId, alias, operationTime);
        syncStateService.syncLatestOperationTime(flinkJobId, alias, operationTime);
    }

    @GetMapping("compaction_start")
    public void compactionStart(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("cluster") String cluster,
            @RequestParam("application_id") String applicationId
    ) {
        logger.info(makeMarker(flinkJobId, alias), "Compaction start: {} {} {} {}", flinkJobId, alias, cluster, applicationId);
        syncStateService.compactionStart(flinkJobId, alias, cluster, applicationId);
    }

    @PostMapping("compaction_pre_commit")
    public void compactionPreCommit(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("instant") String instant,
            @RequestParam("cluster") String cluster,
            @RequestParam("application_id") String applicationId,
            @RequestBody String metadataText
    ) throws JsonProcessingException {
        logger.info(makeMarker(flinkJobId, alias), "Compaction pre commit: {} {} {} {} {}", flinkJobId, alias, instant, cluster, applicationId);
        HoodieCommitMetadata metadata = mapper.readValue(metadataText, HoodieCommitMetadata.class);
        syncStateService.compactionCommit(
                flinkJobId,
                alias,
                "pre",
                instant,
                cluster,
                applicationId,
                metadata,
                metadataText
        );
    }

    @PostMapping("compaction_commit")
    public void compactionCommit(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("instant") String instant,
            @RequestParam("cluster") String cluster,
            @RequestParam("application_id") String applicationId,
            @RequestBody String metadataText
    ) throws JsonProcessingException {
        logger.info(makeMarker(flinkJobId, alias), "Compaction commit: {} {} {} {} {}", flinkJobId, alias, instant, cluster, applicationId);
        HoodieCommitMetadata metadata = mapper.readValue(metadataText, HoodieCommitMetadata.class);
        syncStateService.compactionFinish(flinkJobId, alias);
        syncStateService.compactionCommit(
                flinkJobId,
                alias,
                "complete",
                instant,
                cluster,
                applicationId,
                metadata,
                metadataText
        );
    }

    @PostMapping("compaction_finish")
    public void compactionFinish(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("state") Boolean success,
            @RequestBody(required = false) String exception
    ) {
        logger.info(makeMarker(flinkJobId, alias), "Compaction finish: {} {} {}", flinkJobId, alias, success);
        syncStateService.compactionFinish(flinkJobId, alias, StrUtil.isBlank(exception) ? null : exception);
    }

    @GetMapping("version_update")
    public void versionUpdate(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("version") String version,
            @RequestParam("operation_time") String operationTime
    ) {
        logger.info(makeMarker(flinkJobId, alias), "Version update: {} {} {} {}", flinkJobId, alias, version, operationTime);
        versionUpdateService.versionUpdate(flinkJobId, alias, version, operationTime);
    }

    @GetMapping("compaction_latest_operation_time")
    public void compactionLatestOperationTime(
            @RequestParam("flink_job_id") Long flinkJobId,
            @RequestParam("alias") String alias,
            @RequestParam("operation_time") Long operationTime
    ) {
        logger.info(makeMarker(flinkJobId, alias), "Compaction latest operation time: {} {} {}", flinkJobId, alias, operationTime);
        syncStateService.compactionLatestOperationTime(flinkJobId, alias, operationTime);
    }
}
