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

import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import com.eshore.gringotts.web.configuration.amis.AmisListResponse;
import com.eshore.gringotts.web.configuration.amis.AmisResponse;
import com.eshore.gringotts.web.domain.resource.entity.DataResource;
import com.eshore.gringotts.web.domain.resource.entity.format.CsvResourceFormat;
import com.eshore.gringotts.web.domain.resource.entity.format.JsonLineResourceFormat;
import com.eshore.gringotts.web.domain.resource.entity.format.JsonResourceFormat;
import com.eshore.gringotts.web.domain.resource.entity.format.LineResourceFormat;
import com.eshore.gringotts.web.domain.resource.entity.format.NoneResourceFormat;
import com.eshore.gringotts.web.domain.resource.entity.format.ResourceFormat;
import com.eshore.gringotts.web.domain.resource.entity.type.ApiResourceType;
import com.eshore.gringotts.web.domain.resource.entity.type.DatabaseResourceType;
import com.eshore.gringotts.web.domain.resource.entity.type.FileResourceType;
import com.eshore.gringotts.web.domain.resource.entity.type.FtpResourceType;
import com.eshore.gringotts.web.domain.resource.entity.type.HDFSResourceType;
import com.eshore.gringotts.web.domain.resource.entity.type.ResourceType;
import com.eshore.gringotts.web.domain.resource.service.DataResourceService;
import com.eshore.gringotts.web.domain.upload.entity.DataFile;
import com.eshore.gringotts.web.domain.upload.service.DataFileService;
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.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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.RestController;

/**
 * 数据资源
 *
 * @author lanyuanxiaoyao
 * @date 2024-11-20
 */
@Slf4j
@RestController
@RequestMapping("/data_resource")
public class DataResourceController {
    private final ObjectMapper mapper;
    private final DataResourceService dataResourceService;
    private final DataFileService dataFileService;

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

    @PostMapping("/create")
    public void create(@RequestBody CreateRequest request) throws JsonProcessingException {
        log.info("Create request: {}", request);
        ResourceType type = request.generateResourceType(dataFileService);
        ResourceFormat format = request.generateResourceFormat(mapper);
        dataResourceService.create(request.name, request.description, format, type, dataFileService.detail(request.exampleFileId));
    }

    @PostMapping("/list")
    public AmisListResponse list() {
        return AmisResponse.responseListData(dataResourceService.list().collect(DataResourceListItem::new));
    }

    @GetMapping("/detail/{id}")
    public AmisResponse<DataResourceDetail> detail(@PathVariable Long id) throws JsonProcessingException {
        return AmisResponse.responseSuccess(new DataResourceDetail(mapper, dataResourceService.detail(id)));
    }

    @PostMapping("/update/{id}")
    public void update(@PathVariable Long id, @RequestBody UpdateRequest request) throws JsonProcessingException {

    }

    @Data
    public static class CreateRequest {
        protected String name;
        protected String description;
        protected ResourceType.Type resourceType;
        protected String apiUrl;
        protected String apiUsername;
        protected String apiPassword;
        protected String fileId;
        protected String databaseType;
        protected String databaseJdbc;
        protected String databaseUsername;
        protected String databasePassword;
        protected String coreSiteFileId;
        protected String hdfsSiteFileId;
        protected String ftpUrl;
        protected String ftpUsername;
        protected String ftpPassword;
        protected String ftpPath;
        protected String ftpRegexFilter;
        protected ResourceFormat.Type formatType;
        protected Map<?, ?> jsonSchema;
        protected String jsonSchemaText;
        protected Map<?, ?> jsonLineSchema;
        protected String jsonLineSchemaText;
        protected Map<?, ?> csvSchema;
        protected String csvSchemaText;
        protected String exampleFileId;

        public ResourceType generateResourceType(DataFileService dataFileService) {
            ResourceType type = null;
            switch (resourceType) {
                case API:
                    type = new ApiResourceType(apiUrl, apiUsername, apiPassword);
                    break;
                case FILE:
                    DataFile dataFile = dataFileService.detail(fileId);
                    log.info("{}", dataFile);
                    type = new FileResourceType(dataFile);
                    break;
                case DATABASE:
                    type = new DatabaseResourceType(
                            databaseJdbc,
                            databaseUsername,
                            databasePassword,
                            EnumUtil.fromString(DatabaseResourceType.DatabaseType.class, databaseType)
                    );
                    break;
                case HDFS:
                    type = new HDFSResourceType(dataFileService.detail(coreSiteFileId), dataFileService.detail(hdfsSiteFileId));
                    break;
                case FTP:
                    type = new FtpResourceType(ftpUrl, ftpUsername, ftpPassword, ftpPath, ftpRegexFilter);
                    break;
            }
            return type;
        }

        public ResourceFormat generateResourceFormat(ObjectMapper mapper) throws JsonProcessingException {
            ResourceFormat format = null;
            switch (formatType) {
                case NONE:
                    format = new NoneResourceFormat();
                    break;
                case LINE:
                    format = new LineResourceFormat();
                    break;
                case JSON:
                    format = new JsonResourceFormat(mapper.writeValueAsString(jsonSchema));
                    break;
                case JSON_LINE:
                    format = new JsonLineResourceFormat(mapper.writeValueAsString(jsonLineSchema));
                    break;
                case CSV:
                    format = new CsvResourceFormat(mapper.writeValueAsString(csvSchema));
                    break;
            }
            return format;
        }
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public static final class UpdateRequest extends CreateRequest {
        private Long id;
    }

    @Data
    public static final class DataResourceListItem {
        private Long id;
        private String name;
        private String description;
        private String type;
        private String format;
        private String createdUser;
        private LocalDateTime createdTime;

        public DataResourceListItem(DataResource dataResource) {
            this.id = dataResource.getId();
            this.name = dataResource.getName();
            this.description = dataResource.getDescription();
            this.type = dataResource.getType().getResourceType().name();
            this.format = dataResource.getFormat().getFormatType().name();
            this.createdUser = dataResource.getCreatedUser().getUsername();
            this.createdTime = dataResource.getCreatedTime();
        }
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    @NoArgsConstructor
    public static final class DataResourceDetail extends CreateRequest {
        private String filename;
        private String coreSiteFilename;
        private String hdfsSiteFilename;
        private String exampleFilename;
        private LocalDateTime createdTime;
        private String createdUsername;
        private LocalDateTime modifiedTime;
        private String modifiedUsername;

        public DataResourceDetail(ObjectMapper mapper, DataResource dataResource) throws JsonProcessingException {
            log.info("DataResourceDetail: {}", dataResource);
            this.name = dataResource.getName();
            this.description = dataResource.getDescription();
            this.resourceType = dataResource.getType().getResourceType();
            switch (dataResource.getType().getResourceType()) {
                case API:
                    ApiResourceType apiType = (ApiResourceType) dataResource.getType();
                    this.apiUrl = apiType.getUrl();
                    this.apiUsername = apiType.getUsername();
                    this.apiPassword = apiType.getPassword();
                    break;
                case FILE:
                    FileResourceType fileType = (FileResourceType) dataResource.getType();
                    this.fileId = fileType.getFile().getId().toString();
                    this.filename = fileType.getFile().getFilename();
                    break;
                case DATABASE:
                    DatabaseResourceType databaseType = (DatabaseResourceType) dataResource.getType();
                    this.databaseType = databaseType.getDatabaseType().name();
                    this.databaseJdbc = databaseType.getJdbc();
                    this.databaseUsername = databaseType.getUsername();
                    this.databasePassword = databaseType.getPassword();
                    break;
                case HDFS:
                    HDFSResourceType hdfsType = (HDFSResourceType) dataResource.getType();
                    this.coreSiteFileId = hdfsType.getCoreSite().getId().toString();
                    this.coreSiteFilename = hdfsType.getCoreSite().getFilename();
                    this.hdfsSiteFileId = hdfsType.getHdfsSite().getId().toString();
                    this.hdfsSiteFilename = hdfsType.getHdfsSite().getFilename();
                    break;
                case FTP:
                    FtpResourceType ftpType = (FtpResourceType) dataResource.getType();
                    this.ftpUrl = ftpType.getUrl();
                    this.ftpUsername = ftpType.getUsername();
                    this.ftpPassword = ftpType.getPassword();
                    this.ftpPath = ftpType.getPath();
                    this.ftpRegexFilter = ftpType.getRegexFilter();
                    break;
            }
            this.formatType = dataResource.getFormat().getFormatType();
            switch (dataResource.getFormat().getFormatType()) {
                case NONE:
                case LINE:
                    break;
                case JSON:
                    JsonResourceFormat jsonFormat = (JsonResourceFormat) dataResource.getFormat();
                    this.jsonSchemaText = jsonFormat.getSchema();
                    this.jsonSchema = mapper.readValue(jsonFormat.getSchema(), Map.class);
                    break;
                case JSON_LINE:
                    JsonLineResourceFormat jsonLineFormat = (JsonLineResourceFormat) dataResource.getFormat();
                    this.jsonLineSchemaText = jsonLineFormat.getSchema();
                    this.jsonLineSchema = mapper.readValue(jsonLineFormat.getSchema(), Map.class);
                    break;
                case CSV:
                    CsvResourceFormat csvFormat = (CsvResourceFormat) dataResource.getFormat();
                    this.csvSchemaText = csvFormat.getSchema();
                    this.csvSchema = mapper.readValue(csvFormat.getSchema(), Map.class);
                    break;
            }
            if (ObjectUtil.isNotNull(dataResource.getExample())) {
                this.exampleFileId = dataResource.getExample().getId().toString();
                this.exampleFilename = dataResource.getExample().getFilename();
            }

            this.createdUsername = dataResource.getCreatedUser().getUsername();
            this.createdTime = dataResource.getCreatedTime();
            this.modifiedUsername = dataResource.getModifiedUser().getUsername();
            this.modifiedTime = dataResource.getModifiedTime();
        }
    }
}
