package com.eshore.gringotts.web.domain.controller;

import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import com.eshore.gringotts.web.configuration.amis.AmisResponse;
import com.eshore.gringotts.web.domain.base.controller.SimpleControllerSupport;
import com.eshore.gringotts.web.domain.base.entity.FileInfo;
import com.eshore.gringotts.web.domain.base.entity.SimpleListItem;
import com.eshore.gringotts.web.domain.base.entity.SimpleSaveItem;
import com.eshore.gringotts.web.domain.entity.Confirmation;
import com.eshore.gringotts.web.domain.entity.DataFile;
import com.eshore.gringotts.web.domain.entity.DataResource;
import com.eshore.gringotts.web.domain.entity.format.CsvResourceFormat;
import com.eshore.gringotts.web.domain.entity.format.JsonLineResourceFormat;
import com.eshore.gringotts.web.domain.entity.format.JsonResourceFormat;
import com.eshore.gringotts.web.domain.entity.format.LineResourceFormat;
import com.eshore.gringotts.web.domain.entity.format.NoneResourceFormat;
import com.eshore.gringotts.web.domain.entity.format.ResourceFormat;
import com.eshore.gringotts.web.domain.entity.type.ApiResourceType;
import com.eshore.gringotts.web.domain.entity.type.DatabaseResourceType;
import com.eshore.gringotts.web.domain.entity.type.FileResourceType;
import com.eshore.gringotts.web.domain.entity.type.FtpResourceType;
import com.eshore.gringotts.web.domain.entity.type.HDFSResourceType;
import com.eshore.gringotts.web.domain.entity.type.ResourceType;
import com.eshore.gringotts.web.domain.service.DataFileService;
import com.eshore.gringotts.web.domain.service.DataResourceService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.LocalDateTime;
import java.util.Map;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.collections.api.list.ImmutableList;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 数据资源
 *
 * @author lanyuanxiaoyao
 * @date 2024-11-20
 */
@Slf4j
@RestController
@RequestMapping("/data_resource")
public class DataResourceController extends SimpleControllerSupport<DataResource, DataResourceController.SaveItem, DataResourceController.ListItem, DataResourceController.DetailItem> {
    private final ObjectMapper mapper;
    private final DataResourceService dataResourceService;
    private final DataFileService dataFileService;

    public DataResourceController(DataResourceService dataResourceService, DataFileService dataFileService, Jackson2ObjectMapperBuilder builder) {
        super(dataResourceService);
        this.dataResourceService = dataResourceService;
        this.dataFileService = dataFileService;
        this.mapper = builder.build();
    }

    @GetMapping(LIST + "_no_confirmation")
    public AmisResponse<ImmutableList<ListItem>> listNoConfirmation() {
        return AmisResponse.responseSuccess(dataResourceService.listNoConfirmation().collect(entity -> {
            try {
                return toListItem(entity);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }));
    }

    @GetMapping(LIST + "_no_authentication")
    public AmisResponse<ImmutableList<ListItem>> listNoAuthentication() {
        return AmisResponse.responseSuccess(dataResourceService.listNoAuthentication().collect(entity -> {
            try {
                return toListItem(entity);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }));
    }

    @GetMapping(LIST + "_no_ware")
    public AmisResponse<ImmutableList<ListItem>> listNoWare() {
        return AmisResponse.responseSuccess(dataResourceService.listNoWare().collect(entity -> {
            try {
                return toListItem(entity);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }));
    }

    @Override
    protected DataResource fromSaveItem(SaveItem item) throws JsonProcessingException {
        ResourceType type = generateResourceType(item);
        ResourceFormat format = generateResourceFormat(item);
        DataResource dataResource = new DataResource();
        dataResource.setId(item.getId());
        dataResource.setName(item.getName());
        dataResource.setDescription(item.getDescription());
        dataResource.setType(type);
        dataResource.setFormat(format);
        if (ObjectUtil.isNotNull(item.getExampleFile())) {
            dataResource.setExample(dataFileService.detail(item.getExampleFile().getValue()));
        }
        return dataResource;
    }

    @Override
    protected ListItem toListItem(DataResource dataResource) {
        ListItem item = new ListItem();
        item.setId(dataResource.getId());
        item.setName(dataResource.getName());
        item.setDescription(dataResource.getDescription());
        item.setType(dataResource.getType().getResourceType().name());
        item.setFormat(dataResource.getFormat().getFormatType().name());
        if (ObjectUtil.isNotNull(dataResource.getConfirmation())) {
            item.setConfirmationId(dataResource.getConfirmation().getId());
            item.setConfirmationState(dataResource.getConfirmation().getState());
        } else {
            item.setConfirmationState(Confirmation.State.NONE);
        }
        item.setCreatedUser(dataResource.getCreatedUser().getUsername());
        item.setCreatedTime(dataResource.getCreatedTime());
        return item;
    }

    @Override
    protected DetailItem toDetailItem(DataResource dataResource) throws Exception {
        DetailItem item = new DetailItem();
        item.setId(dataResource.getId());
        item.setResourceTypeId(dataResource.getType().getId());
        item.setResourceFormatId(dataResource.getFormat().getId());
        item.setName(dataResource.getName());
        item.setDescription(dataResource.getDescription());
        item.setResourceType(dataResource.getType().getResourceType());
        switch (dataResource.getType().getResourceType()) {
            case API:
                ApiResourceType apiType = (ApiResourceType) dataResource.getType();
                item.setApiUrl(apiType.getUrl());
                item.setApiUsername(apiType.getUsername());
                item.setApiPassword(apiType.getPassword());
                break;
            case FILE:
                FileResourceType fileType = (FileResourceType) dataResource.getType();
                item.setFile(new FileInfo(fileType.getFile()));
                break;
            case DATABASE:
                DatabaseResourceType databaseType = (DatabaseResourceType) dataResource.getType();
                item.setDatabaseType(databaseType.getDatabaseType().name());
                item.setDatabaseJdbc(databaseType.getJdbc());
                item.setDatabaseUsername(databaseType.getUsername());
                item.setDatabasePassword(databaseType.getPassword());
                break;
            case HDFS:
                HDFSResourceType hdfsType = (HDFSResourceType) dataResource.getType();
                item.setCoreSiteFile(new FileInfo(hdfsType.getCoreSite()));
                item.setHdfsSiteFile(new FileInfo(hdfsType.getHdfsSite()));
                break;
            case FTP:
                FtpResourceType ftpType = (FtpResourceType) dataResource.getType();
                item.setFtpUrl(ftpType.getUrl());
                item.setFtpUsername(ftpType.getUsername());
                item.setFtpPassword(ftpType.getPassword());
                item.setFtpPath(ftpType.getPath());
                item.setFtpRegexFilter(ftpType.getRegexFilter());
                break;
        }
        item.setFormatType(dataResource.getFormat().getFormatType());
        switch (dataResource.getFormat().getFormatType()) {
            case NONE:
            case LINE:
                break;
            case JSON:
                JsonResourceFormat jsonFormat = (JsonResourceFormat) dataResource.getFormat();
                item.setJsonSchemaText(jsonFormat.getSchema());
                item.setJsonSchema(mapper.readValue(jsonFormat.getSchema(), Map.class));
                break;
            case JSON_LINE:
                JsonLineResourceFormat jsonLineFormat = (JsonLineResourceFormat) dataResource.getFormat();
                item.setJsonLineSchemaText(jsonLineFormat.getSchema());
                item.setJsonLineSchema(mapper.readValue(jsonLineFormat.getSchema(), Map.class));
                break;
            case CSV:
                CsvResourceFormat csvFormat = (CsvResourceFormat) dataResource.getFormat();
                item.setCsvSchemaText(csvFormat.getSchema());
                item.setCsvSchema(mapper.readValue(csvFormat.getSchema(), Map.class));
                break;
        }
        if (ObjectUtil.isNotNull(dataResource.getExample())) {
            item.setExampleFile(new FileInfo(dataResource.getExample()));
        }
        item.setCreatedUsername(dataResource.getCreatedUser().getUsername());
        item.setCreatedTime(dataResource.getCreatedTime());
        item.setModifiedUsername(dataResource.getModifiedUser().getUsername());
        item.setModifiedTime(dataResource.getModifiedTime());
        return item;
    }

    private ResourceType generateResourceType(SaveItem item) {
        ResourceType type = null;
        switch (item.getResourceType()) {
            case API:
                type = new ApiResourceType(item.getApiUrl(), item.getApiUsername(), item.getApiPassword());
                break;
            case FILE:
                DataFile dataFile = dataFileService.detail(item.getFile().getValue());
                type = new FileResourceType(dataFile);
                break;
            case DATABASE:
                type = new DatabaseResourceType(
                        item.getDatabaseJdbc(),
                        item.getDatabaseUsername(),
                        item.getDatabasePassword(),
                        EnumUtil.fromString(DatabaseResourceType.DatabaseType.class, item.getDatabaseType())
                );
                break;
            case HDFS:
                type = new HDFSResourceType(dataFileService.detail(item.getCoreSiteFile().getValue()), dataFileService.detail(item.getHdfsSiteFile().getValue()));
                break;
            case FTP:
                type = new FtpResourceType(item.getFtpUrl(), item.getFtpUsername(), item.getFtpPassword(), item.getFtpPath(), item.getFtpRegexFilter());
                break;
        }
        type.setId(item.getResourceTypeId());
        return type;
    }

    private ResourceFormat generateResourceFormat(SaveItem item) throws JsonProcessingException {
        ResourceFormat format = null;
        switch (item.getFormatType()) {
            case NONE:
                format = new NoneResourceFormat();
                break;
            case LINE:
                format = new LineResourceFormat();
                break;
            case JSON:
                format = new JsonResourceFormat(mapper.writeValueAsString(item.getJsonSchema()));
                break;
            case JSON_LINE:
                format = new JsonLineResourceFormat(mapper.writeValueAsString(item.getJsonLineSchema()));
                break;
            case CSV:
                format = new CsvResourceFormat(mapper.writeValueAsString(item.getCsvSchema()));
                break;
        }
        format.setId(item.getResourceFormatId());
        return format;
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public static class SaveItem extends SimpleSaveItem<DataResource> {
        private String name;
        private String description;
        private Long resourceTypeId;
        private ResourceType.Type resourceType;
        private String apiUrl;
        private String apiUsername;
        private String apiPassword;
        private FileInfo file;
        private String databaseType;
        private String databaseJdbc;
        private String databaseUsername;
        private String databasePassword;
        private FileInfo coreSiteFile;
        private FileInfo hdfsSiteFile;
        private String ftpUrl;
        private String ftpUsername;
        private String ftpPassword;
        private String ftpPath;
        private String ftpRegexFilter;
        private Long resourceFormatId;
        private ResourceFormat.Type formatType;
        private Map<?, ?> jsonSchema;
        private String jsonSchemaText;
        private Map<?, ?> jsonLineSchema;
        private String jsonLineSchemaText;
        private Map<?, ?> csvSchema;
        private String csvSchemaText;
        private FileInfo exampleFile;
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public static final class ListItem extends SimpleListItem<DataResource> {
        private Long id;
        private String name;
        private String description;
        private String type;
        private String format;
        private Long confirmationId;
        private Confirmation.State confirmationState;
        private String createdUser;
        private LocalDateTime createdTime;
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public static final class DetailItem extends SaveItem {
        private LocalDateTime createdTime;
        private String createdUsername;
        private LocalDateTime modifiedTime;
        private String modifiedUsername;
    }
}
