package com.qqchen.deploy.backend.deploy.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.deploy.converter.ApplicationConverter;
import com.qqchen.deploy.backend.deploy.converter.DeployLogConverter;
import com.qqchen.deploy.backend.deploy.dto.BuildConfigDefinedDTO;
import com.qqchen.deploy.backend.deploy.dto.DeployAppBuildDTO;
import com.qqchen.deploy.backend.deploy.dto.DeployAppConfigDTO;
import com.qqchen.deploy.backend.deploy.dto.DeployLogDTO;
import com.qqchen.deploy.backend.deploy.entity.Application;
import com.qqchen.deploy.backend.deploy.entity.DeployAppConfig;
import com.qqchen.deploy.backend.deploy.entity.DeployLog;
import com.qqchen.deploy.backend.deploy.entity.Environment;
import com.qqchen.deploy.backend.deploy.enums.BuildTypeEnum;
import com.qqchen.deploy.backend.deploy.enums.DevelopmentLanguageTypeEnum;
import com.qqchen.deploy.backend.deploy.query.DeployAppConfigQuery;
import com.qqchen.deploy.backend.deploy.repository.IApplicationRepository;
import com.qqchen.deploy.backend.deploy.repository.IDeployLogRepository;
import com.qqchen.deploy.backend.deploy.repository.IEnvironmentRepository;
import com.qqchen.deploy.backend.deploy.service.IDeployAppConfigService;
import com.qqchen.deploy.backend.deploy.service.IDeployLogService;
import com.qqchen.deploy.backend.framework.annotation.formily.FormilySchemaFactory;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.converter.WorkflowDefinitionConverter;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
import com.qqchen.deploy.backend.workflow.util.GenerateSchemaUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static java.util.stream.Collectors.toList;

/**
 * 应用配置服务实现类
 */
@Slf4j
@Service
public class DeployAppConfigServiceImpl extends BaseServiceImpl<DeployAppConfig, DeployAppConfigDTO, DeployAppConfigQuery, Long> implements IDeployAppConfigService {

    @Resource
    private IApplicationRepository applicationRepository;

    @Resource
    private ApplicationConverter applicationConverter;

    @Resource
    private IWorkflowDefinitionRepository workflowDefinitionRepository;

    @Resource
    private IWorkflowInstanceService workflowInstanceService;

    @Resource
    private WorkflowDefinitionConverter workflowDefinitionConverter;

    @Resource
    private IEnvironmentRepository environmentRepository;

    @Resource
    private IDeployLogService deployLogService;

    @Resource
    private IDeployLogRepository deployLogRepository;

    @Resource
    private DeployLogConverter deployLogConverter;

    @Resource
    private IWorkflowInstanceRepository workflowInstanceRepository;

    @Resource
    private ObjectMapper objectMapper;

    @Override
    public Page<DeployAppConfigDTO> page(DeployAppConfigQuery query) {
        Page<DeployAppConfigDTO> page = super.page(query);
        List<DeployAppConfigDTO> result = page.getContent().stream().peek(deployAppConfig -> {
            Optional<WorkflowDefinition> optionalWorkflowDefinition = workflowDefinitionRepository.findById(deployAppConfig.getWorkflowDefinitionId());
            optionalWorkflowDefinition.ifPresent(workflowDefinition -> deployAppConfig.setPublishedWorkflowDefinition(workflowDefinitionConverter.toDto(workflowDefinition)));
            Optional<Application> optionalApplication = applicationRepository.findById(deployAppConfig.getApplicationId());
            if (optionalApplication.isPresent()) {
                Application application = optionalApplication.get();
                deployAppConfig.setApplication(applicationConverter.toDto(application));
                Optional<DeployLog> lastDeployLogOptional = deployLogRepository.findTop1ByApplicationIdOrderByCreateTimeDesc(application.getId());
                if (lastDeployLogOptional.isEmpty()) {
                    return;
                }
                DeployLog lastDeployLog = lastDeployLogOptional.get();
                Optional<WorkflowInstance> optionalWorkflowInstance = workflowInstanceRepository.findById(lastDeployLog.getWorkflowInstanceId());
                if (optionalWorkflowInstance.isEmpty()) {
                    return;
                }
                WorkflowInstance workflowInstance = optionalWorkflowInstance.get();
                deployAppConfig.setLastBuildStatus(workflowInstance.getStatus());
                deployAppConfig.setLastBuildStartTime(workflowInstance.getStartTime());
                deployAppConfig.setLastBuildEndTime(workflowInstance.getEndTime());
            }
        }).collect(toList());
        return new PageImpl<>(result, page.getPageable(), page.getTotalElements());
    }

    @Override
    public List<BuildConfigDefinedDTO> defined() {
        List<BuildConfigDefinedDTO> result = new ArrayList<>();

        for (BuildTypeEnum buildType : BuildTypeEnum.values()) {
            try {
                // 获取支持的语言和对应的构建变量类
                DevelopmentLanguageTypeEnum[] languages = buildType.getSupportedLanguages();
                Class<?>[] buildVariablesClasses = buildType.getBuildVariables();
                // 如果支持的语言不为空，则处理每种语言对应的Schema
                if (languages != null && buildVariablesClasses != null) {
                    for (int i = 0; i < languages.length; i++) {
                        if (buildVariablesClasses[i] != null) {
                            BuildConfigDefinedDTO definedDTO = new BuildConfigDefinedDTO();
                            definedDTO.setCode(buildType.getCode());
                            definedDTO.setName(buildType.getName());
                            definedDTO.setBuildType(buildType);
                            definedDTO.setLanguageType(languages[i]);
                            // 使用新的FormilySchemaFactory生成Schema
                            JsonNode schema = FormilySchemaFactory.generateSchema(buildVariablesClasses[i]);
                            definedDTO.setBuildVariablesSchema(schema);
                            result.add(definedDTO);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("Error processing build type: " + buildType, e);
            }
        }
        return result;
    }

    @Override
    @Transactional
    public void deploy(DeployAppBuildDTO dto) {
        WorkflowDefinition workflowDefinition = workflowDefinitionRepository.findById(dto.getWorkflowDefinitionId()).orElseThrow(() -> new RuntimeException("Workflow definition not found: " + dto.getWorkflowDefinitionId()));
        Environment environment = environmentRepository.findById(dto.getEnvironmentId()).orElseThrow(() -> new RuntimeException("Environment not found: " + dto.getEnvironmentId()));
        Application application = applicationRepository.findById(dto.getApplicationId()).orElseThrow(() -> new RuntimeException("Application not found: " + dto.getApplicationId()));
        WorkflowInstanceStartRequest request = new WorkflowInstanceStartRequest();
        request.setProcessKey(workflowDefinition.getKey());
        request.setBusinessKey(environment.getEnvCode() + "_" + application.getAppCode() + "_" + System.currentTimeMillis() / 1000);
        request.setVariables(objectMapper.convertValue(dto.getBuildVariables(), new TypeReference<>() {
        }));
        WorkflowInstanceDTO workflowInstanceDTO = workflowInstanceService.startWorkflow(request);
        buildAndSaveDeployLog(dto, environment, application, workflowInstanceDTO);
    }

    private void buildAndSaveDeployLog(DeployAppBuildDTO deployAppBuild, Environment environment, Application application, WorkflowInstanceDTO workflowInstance) {
        DeployLogDTO deployLog = new DeployLogDTO();
        deployLog.setEnvironmentId(environment.getId());
        deployLog.setApplicationId(application.getId());
        deployLog.setWorkflowInstanceId(workflowInstance.getId());
        deployLog.setVariables(deployAppBuild.getBuildVariables());
        deployLog.setDeployVersion(application.getAppCode() + "_" + System.currentTimeMillis() / 1000);
        deployLog.setDeployVariables(deployAppBuild.getBuildVariables());
        deployLog.setFormVariables(deployAppBuild.getFormVariables());
        deployLogService.create(deployLog);
    }

    @Override
    public DeployAppConfigDTO create(DeployAppConfigDTO dto) {
        DeployAppConfig entity = converter.toEntity(dto);
        // 使用新的FormilySchemaFactory生成Schema
        entity.setFormVariablesSchema(GenerateSchemaUtils.generateSchema(dto.getBuildType().getFormVariablesSchema()));
        this.repository.save(entity);
        return converter.toDto(entity);
    }
}