package io.renren.modules.sys.controller.algs;

import io.minio.errors.*;
import io.renren.common.annotation.SysLog;
import io.renren.common.utils.Constant;
import io.renren.common.utils.MinIoUtils;
import io.renren.common.utils.PageUtils;
import io.renren.common.utils.R;
import io.renren.common.validator.ValidatorUtils;
import io.renren.common.validator.group.AddGroup;
import io.renren.common.validator.group.UpdateGroup;
import io.renren.modules.sys.entity.algs.*;
import io.renren.modules.sys.entity.dataset.DataSet;
import io.renren.modules.sys.service.*;
import io.renren.modules.sys.service.impl.AlgsModelsServiceImpl;
import io.renren.modules.sys.service.impl.AlgsServiceImpl;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static io.renren.common.utils.ShiroUtils.getUserId;

/**
 * @Author: Ivan Q
 * @Date: 2021/6/10 20:56
 */

@RestController
@RequestMapping("/algs")
public class algsController{

    @Autowired
    AlgsServiceImpl algsService;
    @Autowired
    VersionService versionService;
    @Autowired
    DataSetService dataSetService;
    @Autowired
    AlgorithmParameterService algorithmParameterService;
    @Autowired
    AlgorithmResultService algorithmResultService;
    /**
     * 所有列表
     */
    @GetMapping("/list")
    /*
    把这个@RequiresPermissions注解去掉，那么如果在角色管理中，去掉查看功能的话也可以获取列表，但是没有每条数据右侧的查看按钮；
    不去掉的话那就是必须在打开查看模板功能，否则不能获取列表数据。
     */
    @RequiresPermissions("algs:list")
    public R list(@RequestParam Map<String, Object> params){
        //只有超级管理员，才能查看所有模板列表
        //先这样写，之后去掉
        if(getUserId() != Constant.SUPER_ADMIN){
            params.put("createUserId", getUserId());
        }
        PageUtils page = algsService.queryPageOnUse(params);

        return R.ok().put("page", page);
    }

    @GetMapping("/selectOnCreateMission")
    public R selectOnCreateMission(@RequestParam Map<String, Object> params){
        List<Algorithm_Vo> list=algsService.selectOnCreateMission(params);
        return R.ok().put("list",list);
    }

    /**
     * 信息
     */
    @GetMapping("/info/{algsId}")
    @RequiresPermissions("algs:info")
    public R info(@PathVariable("algsId") Long algsId) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, ErrorResponseException, XmlParserException, InvalidBucketNameException, InternalException {
        Algorithm algs = algsService.getById(algsId);
//        Long categoryId = algsTemplsService.querycategoryId(tmpId);
//        temp.setCategoryId((categoryId));

//        不需要获取创建人的信息，总的列表中已经有了
//        String username = userService.getById(temp.getUid()).getUsername();
        List<FileTest> list=MinIoUtils.listFiles("algorithm","alg"+algsId);
        return R.ok().put("algs", algs).put("fileList",list);
    }

    /**
     * 保存算法
     *
     */
    @SysLog("保存算法")
    @PostMapping("/save")
    @RequiresPermissions("algs:save")
    public R save(MultipartHttpServletRequest request) throws IOException, InvalidResponseException, InvalidKeyException, NoSuchAlgorithmException, InternalException, XmlParserException, ErrorResponseException, InvalidBucketNameException, InsufficientDataException, RegionConflictException {
        Algorithm alg=new Algorithm();
        alg.setAlgorithmName(request.getParameter("algorithmName"));
        alg.setRemark(request.getParameter("remark"));
        alg.setCategoryId(Long.parseLong(request.getParameter("categoryId")));
        alg.setUid(Long.parseLong(request.getParameter("uid")));
        alg.setFrameId(Long.parseLong(request.getParameter("frameId")));
        alg.setNumber(Long.parseLong(request.getParameter("number")));

        ValidatorUtils.validateEntity(alg, AddGroup.class);
        List<Algorithm> list = algsService.list();
        for(Algorithm algorithm : list){
            if(algorithm.getAlgorithmName().equals(alg.getAlgorithmName())) return R.error("已经存在重复名称的算法");
        }
//        user.setCreateUserId(getUserId());

        algsService.save(alg);
        //获取存储到数据库后的algId

        Long algId=algsService.selectByAlgName(alg.getAlgorithmName()).getAlgorithmId();
        alg.setAlgorithmId(algId);
        List<MultipartFile> files=request.getFiles("file");
        //若是智能算法，则调用上传智能算法方法,-1为传统算法，其他为智能算法
        if(alg.getFrameId()!=-1){
            String dataSetsString= request.getParameter("dataSets");//如果有选择数据集，则将数据集从minio中拷贝到相应位置
            String algModelsString=request.getParameter("algModels");//如果有选择算法，则将对应算法代码拷贝到相应位置
            String algTemplatesString=request.getParameter("algTemplates");//如果有选择模板，则将对应模板代码拷贝到相应位置
            saveIntelligentAlg(alg,dataSetsString,algModelsString,algTemplatesString,files);
        }else {
            String dataSetsString= request.getParameter("dataSets");//如果有选择数据集，则将数据集从minio中拷贝到相应位置
            String algResultNameString=request.getParameter("algResultNames");
            String algResultLocationString=request.getParameter("algResultLocations");
            String algParameterNameString=request.getParameter("algParameterName");
            String algParameterTypeString=request.getParameter("algParameterTypes");
            saveTraditionalAlg(alg,files,algParameterNameString,algParameterTypeString,algResultNameString,algResultLocationString,dataSetsString);

        }
        return R.ok();
    }

    /**
     * 保存智能算法
     * @param alg 算法对象实体
     * @param dataSetsString 算法引入的数据集id，以逗号分隔
     * @param algModelsString 算法引入的算法库算法id，以逗号分隔
     * @param algTemplatesString 算法引入的算法模板id，以逗号分隔
     * @param files 算法文件
     */
    public void saveIntelligentAlg(Algorithm alg,String dataSetsString,String algModelsString,String algTemplatesString,List<MultipartFile> files) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, ErrorResponseException, XmlParserException, InvalidBucketNameException, InternalException {
        Long algId=alg.getAlgorithmId();
        Version version=new Version();
        version.setAlgorithmId(algId);
        version.setVersionName(alg.getAlgorithmName()+"#"+ (versionService.getMaxId() + 1));
        version.setCreationTime(new Date());
        version.setVersionStatus((byte) 0);
        version.setDeleteStatus((byte) 0);
        versionService.save(version);
        for(MultipartFile file:files){
            if(file.isEmpty())  break;
            MinIoUtils.uploadMultipartFile(file,"algorithm","alg"+algId+"/version"+versionService.getMaxId()+"/"+file.getOriginalFilename());
        }
        if(dataSetsString.length()!=0){
            String[] dataSets=dataSetsString.split(",");
            for(String fileName:dataSets){
                MinIoUtils.copyFile("algorithm","alg"+algId+"/version"+versionService.getMaxId()+"/"+fileName,"dataset",fileName);
            }
        }
        //MinIoUtils.copyFile("algorithm","alg"+algId+"/version"+versionService.getMaxId()+"/fea_data.csv","dataset","fea_data.csv");
        if(algModelsString.length()!=0){
            String[] algModels=algModelsString.split(",");
            for(String algModelId:algModels){
                List<FileTest> algModelFiles=MinIoUtils.listFiles("algmodel","model"+algModelId);
                for(FileTest algModelFile:algModelFiles){
                    if(algModelFile.getFilename().substring(algModelFile.getFilename().lastIndexOf(".")).equals(".py")){
                        MinIoUtils.copyFile("algorithm","alg"+algId+"/version"+versionService.getMaxId()+"/"+algModelFile.getFilename(),"algmodel","model"+algModelId+"/"+algModelFile.getFilename());
                    }
                }
            }
        }
        if(algTemplatesString.length()!=0){
            String[] algTemplates=algTemplatesString.split(",");
            for(String algTemplateId:algTemplates){
                List<FileTest> algTemplateFiles=MinIoUtils.listFiles("algtemplate","template"+algTemplateId);
                for(FileTest algTemplateFile:algTemplateFiles){
                    MinIoUtils.copyFile("algorithm","alg"+algId+"/version"+versionService.getMaxId()+"/"+algTemplateFile.getFilename(),"algtemplate","template"+algTemplateId+"/"+algTemplateFile.getFilename());
                }
            }
        }
    }

    /**
     *
     * @param alg 算法对象实体
     * @param files 算法文件
     */
    public void saveTraditionalAlg(Algorithm alg,List<MultipartFile> files,String algParameterNameString,String algParameterTypeString,String algResultNameString,String algResultLocationString,String dataSetsString) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, ErrorResponseException, XmlParserException, InvalidBucketNameException, InternalException {
        Long algId=alg.getAlgorithmId();

        System.out.println("算法参数名是");
        String[] algorithmParameterNames=algParameterNameString.split(",");
        for(String s:algorithmParameterNames){
            System.out.println(s);
        }

        System.out.println("算法参数类别是");
        String[] algorithmParameterTypes=algParameterTypeString.split(",");
        for(String s:algorithmParameterTypes){
            System.out.println(s);
        }
        for(int i=0;i<algorithmParameterNames.length;i++){
            AlgorithmParameter algorithmParameter=new AlgorithmParameter();
            algorithmParameter.setAlgorithmId(algId);
            algorithmParameter.setAlgorithmParameterName(algorithmParameterNames[i]);
            algorithmParameter.setAlgorithmParameterType(algorithmParameterTypes[i]);
            algorithmParameterService.save(algorithmParameter);
        }

        System.out.println("算法结果名是");
        String[] algorithmResultNames=algResultNameString.split(",");
        for(String s:algorithmResultNames){
            System.out.println(s);
        }
        System.out.println("算法结果文件位置是");
        String[] algorithmResultLocations=algResultLocationString.split(",");
        for(String s:algorithmResultLocations){
            System.out.println(s);
        }
        for(int i=0;i<algorithmResultNames.length;i++){
            AlgorithmResult algorithmResult=new AlgorithmResult();
            algorithmResult.setAlgorithmId(algId);
            algorithmResult.setAlgorithmResultName(algorithmResultNames[i]);
            algorithmResult.setAlgorithmResultLocation(algorithmResultLocations[i]);
            algorithmResultService.save(algorithmResult);
        }
        //将算法文件上传
        for(MultipartFile file:files){
            if(file.isEmpty())  break;
            MinIoUtils.uploadMultipartFile(file,"algorithm","alg"+algId+"/"+file.getOriginalFilename());
        }
        //将数据集上传
        if(dataSetsString.length()!=0){
            String[] dataSets=dataSetsString.split(",");
            for(String fileName:dataSets){
                MinIoUtils.copyFile("algorithm","alg"+algId+"/"+fileName,"dataset",fileName);
            }
        }
    }


    /**
     * 修改算法
     */
    @SysLog("修改算法")
    @PostMapping("/update")
    @RequiresPermissions("algs:update")
    public R update(MultipartHttpServletRequest request){
        List<Algorithm> list = algsService.list();

        Algorithm alg=new Algorithm();
        alg.setAlgorithmId(Long.parseLong(request.getParameter("algorithmId")));
        alg.setAlgorithmName(request.getParameter("algorithmName"));
        alg.setCategoryId(Long.parseLong(request.getParameter("categoryId")));
        alg.setRemark(request.getParameter("remark"));

        ValidatorUtils.validateEntity(alg, UpdateGroup.class);

//        model.setCreateUserId(getUserId());
        for(Algorithm algorithm : list){
            if(algorithm.getAlgorithmName().equals(alg.getAlgorithmName())&&algorithm.getAlgorithmId()!=alg.getAlgorithmId()) return R.error("已经存在重复名称的算法");
        }
        algsService.update(alg);

        return R.ok();
    }

    /**
     * 删除算法
     */
    @SysLog("删除算法")
    @PostMapping("/delete")
    @RequiresPermissions("algs:delete")
    public R delete(@RequestBody Long[] algIds) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, ErrorResponseException, XmlParserException, InvalidBucketNameException, InternalException {
        algsService.deleteBatch(algIds);
        //需添加删除算法时，将该算法的所有版本都删除
        for(Long id:algIds){
            MinIoUtils.deleteFilesByPrefix("algorithm","alg"+id);
        }
        return R.ok();
    }

    @GetMapping("/listFiles")
    public R listFiles(String algId) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, ErrorResponseException, XmlParserException, InvalidBucketNameException, InternalException {

        List<FileTest> fileList= MinIoUtils.listFiles("algorithm","alg"+algId);
        return R.ok().put("fileList",fileList);
    }
}
