package jj.tech.paolu.utils.mybatis;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;

import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

import jj.tech.paolu.utils.IDHelp;
import jj.tech.paolu.utils.R;

/**
 * 生成Controller
 * @author Dou
 */
public class MybatisControllerPlugin extends PluginAdapter {


    private String targetProject = "src/main/java"; //"target/generated-sources/repository";

    private String targetPackage = "jj.tech.paolu.repository.mybatis.control";


    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public void setProperties(Properties properties) {
        super.setProperties(properties);
        
//        targetProject = properties.getProperty("targetProject");
//        targetPackage = properties.getProperty("targetPackage");
    }

    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
    	
    	//TopLevelClass
        if (Objects.nonNull(targetPackage) && Objects.nonNull(targetProject)) {
        	
        	String daoMapperName = introspectedTable.getMyBatis3SqlMapNamespace();
        	
        	String recordType = introspectedTable.getBaseRecordType();
            String[] entityPackage = recordType.split("\\.");
            String entityName = entityPackage[entityPackage.length - 1];
            
            String first = String.valueOf(entityName.charAt(0));
            String lowerEntityName = entityName.replaceFirst(first, first.toLowerCase());
            String lowerEntityNameMapper = lowerEntityName + "Mapper";
            
            TopLevelClass controllerClass = new TopLevelClass(targetPackage + "." + entityName + "Controller");
            
            controllerClass.addImportedType("java.lang.Object");
            controllerClass.addImportedType("org.springframework.web.bind.annotation.*");
            //controllerClass.addImportedType("org.springframework.stereotype.Controller");
            controllerClass.addImportedType(new FullyQualifiedJavaType("org.springframework.beans.factory.annotation.Autowired"));
            controllerClass.addImportedType(daoMapperName);
            controllerClass.addImportedType(introspectedTable.getBaseRecordType());
            controllerClass.addImportedType("jj.tech.paolu.utils.R");
            controllerClass.addImportedType("jj.tech.paolu.utils.IDHelp");
            controllerClass.addAnnotation("@RestController");
            controllerClass.setVisibility(JavaVisibility.PUBLIC);
            //controllerClass.addAnnotation("@RestController");
            
            
            controllerClass.addAnnotation("@RequestMapping(\"mybatis/" + lowerEntityName + "\")");

            //daoMapper inject 
            FullyQualifiedJavaType daoMapper = new FullyQualifiedJavaType(daoMapperName);
            Field serviceField = new Field(lowerEntityNameMapper , daoMapper);
            serviceField.addJavaDocLine("");
            serviceField.addAnnotation("@Autowired");
            serviceField.setVisibility(JavaVisibility.PUBLIC);
            controllerClass.addField(serviceField);
            
            
            // importe  primaryKey type
            List<IntrospectedColumn> idColumns = introspectedTable.getPrimaryKeyColumns();
            if(idColumns==null || idColumns.isEmpty()) {
            	return Collections.emptyList();
            }
            Set<FullyQualifiedJavaType> idColumnsypes = new TreeSet<FullyQualifiedJavaType>();
            idColumns.forEach(idColumn->{
            	idColumnsypes.add(idColumn.getFullyQualifiedJavaType());
            });
            controllerClass.addImportedTypes(idColumnsypes);
            

            //Add Method
            FullyQualifiedJavaType respondJavaType = new FullyQualifiedJavaType("Object");
            controllerClass.addImportedType(respondJavaType);
            //controllerClass.addImportedType(new FullyQualifiedJavaType(recordType));
            
            
            

            //findById
            Method findByIdMethod = new Method("findById");
            findByIdMethod.setVisibility(JavaVisibility.PUBLIC);
            findByIdMethod.setReturnType(respondJavaType);
            
            StringBuilder parmMapping = new StringBuilder();
            StringBuilder parm = new StringBuilder();
            idColumns.forEach(idColumn->{
            	FullyQualifiedJavaType primaryKeyType = idColumn.getFullyQualifiedJavaType();
                String primaryKeyName = idColumn.getJavaProperty();
            	Parameter idParameter = new Parameter(primaryKeyType, primaryKeyName);
            	idParameter.addAnnotation("@PathVariable(\"" + primaryKeyName + "\")");
            	findByIdMethod.addParameter(idParameter);
            	
            	parmMapping.append("/{"+primaryKeyName+"}");
            	parm.append(primaryKeyName+",");
            });
            parm.delete(parm.lastIndexOf(","), parm.length());
            
            findByIdMethod.addAnnotation("@PostMapping(\"findById" + parmMapping + "\")");
            findByIdMethod.addBodyLine("return R.SUCCESS(" + lowerEntityNameMapper+".selectByPrimaryKey("+parm+").orElse(null));");
            controllerClass.addMethod(findByIdMethod);
            
            
            //delete
            Method deleteMethod = new Method("delete");
            deleteMethod.setVisibility(JavaVisibility.PUBLIC);
            deleteMethod.setReturnType(respondJavaType);
            
            StringBuilder parmdeleteMapping = new StringBuilder();
            StringBuilder parmdelete = new StringBuilder();
            idColumns.forEach(idColumn->{
            	FullyQualifiedJavaType primaryKeyType = idColumn.getFullyQualifiedJavaType();
                String primaryKeyName = idColumn.getJavaProperty();
            	Parameter idParameter = new Parameter(primaryKeyType, primaryKeyName);
            	idParameter.addAnnotation("@PathVariable(\"" + primaryKeyName + "\")");
            	deleteMethod.addParameter(idParameter);
            	
            	parmdeleteMapping.append("/{"+primaryKeyName+"}");
            	parmdelete.append(primaryKeyName+",");
            });
            parmdelete.delete(parmdelete.lastIndexOf(","), parmdelete.length());
            
            deleteMethod.addAnnotation("@PostMapping(\"delete" + parmdeleteMapping + "\")");
            deleteMethod.addBodyLine("return R.SUCCESS(" + lowerEntityNameMapper+".deleteByPrimaryKey("+ parmdelete +"));");
            controllerClass.addMethod(deleteMethod);
            
            
            
            //save
            Method saveMethod = new Method("save");
            saveMethod.setVisibility(JavaVisibility.PUBLIC);
            saveMethod.setReturnType(respondJavaType);
            saveMethod.addAnnotation("@PostMapping(\"save\")");
            Parameter saveParameter = new Parameter(new FullyQualifiedJavaType(recordType), "record");
            saveParameter.addAnnotation("@RequestBody");
            saveMethod.addParameter(saveParameter);
            saveMethod.addBodyLine("record.setId(IDHelp.getInstance().nextId());");
            saveMethod.addBodyLine("return R.SUCCESS(" + lowerEntityNameMapper+".insertSelective(record));");
            controllerClass.addMethod(saveMethod);
        
            //update
            Method updateMethod = new Method("update");
            updateMethod.setVisibility(JavaVisibility.PUBLIC);
            updateMethod.setReturnType(respondJavaType);
            updateMethod.addAnnotation("@PostMapping(\"update\")");
            Parameter updateParameter = new Parameter(new FullyQualifiedJavaType(recordType), "record");
            updateParameter.addAnnotation("@RequestBody");
            updateMethod.addParameter(saveParameter);
            updateMethod.addBodyLine("return R.SUCCESS(" + lowerEntityNameMapper+".updateByPrimaryKeySelective(record));");
            controllerClass.addMethod(updateMethod);
           
           
            
            //page
//            Method pageMethod = new Method("page");
//            pageMethod.addAnnotation("@PostMapping(\"page\")");
//            pageMethod.setReturnType(respondJavaType); Parameter pageNum = new
//            Parameter(new FullyQualifiedJavaType("int"), "pageNum");
//            pageNum.addAnnotation("@RequestParam(defaultValue = \"1\")");
//			pageMethod.addParameter(pageNum); Parameter pageSize = new Parameter(new
//			FullyQualifiedJavaType("int"), "pageSize");
//			pageSize.addAnnotation("@RequestParam(defaultValue = \"15\")");
//			pageMethod.addParameter(pageSize); pageMethod.addBodyLine("return  null;");
//			controllerClass.addMethod(pageMethod);
			 

            GeneratedJavaFile javaFile = new GeneratedJavaFile(controllerClass, targetProject,
                    context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
                    context.getJavaFormatter());
            
            List<GeneratedJavaFile> files = new ArrayList<>();
            files.add(javaFile);
            return files;
        }
        return Collections.emptyList();
    }
    
//    public static void main(String[] args) throws Exception {
//    	
//    	List<String> warnings = new ArrayList<String>();
//	    boolean overwrite = true;
//	    File configFile = new File("src/main/resources/generate/mybatis/generatorConfig.xml");
//	    ConfigurationParser cp = new ConfigurationParser(warnings);
//	    Configuration config = cp.parseConfiguration(configFile);
//	    DefaultShellCallback callback = new DefaultShellCallback(overwrite);
//	    MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
//	    myBatisGenerator.generate(null);
//	}

}
