package cn.axzo.workflow.generate;

import cn.axzo.workflow.generate.annotition.GenService;
import cn.axzo.workflow.generate.annotition.Management;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;

@AutoService(CodeGenProcessor.class)
public class GenServiceProcessor extends BaseCodeGenProcessor {

    @Override
    public Class<? extends Annotation> getAnnotation() {
        return GenService.class;
    }

    @Override
    public String generatePackage(TypeElement typeElement) {
        return typeElement.getAnnotation(GenService.class).genPkgName();
    }

    @Override
    public GenCodeInfo generate(TypeElement typeElement, RoundEnvironment roundEnvironment) throws Exception {
        String genPkgName = typeElement.getAnnotation(GenService.class).genPkgName();
        String sourcePath = typeElement.getAnnotation(GenService.class).genSourcePath();
        String genClassName = typeElement.getAnnotation(GenService.class).genClassName();
        List<? extends Element> enclosedElements = typeElement.getEnclosedElements();
        TypeSpec.Builder targetTypeSpec = TypeSpec.interfaceBuilder(genClassName)
                .addModifiers(typeElement.getModifiers().toArray(new Modifier[]{}));
        for (Element enclosedElement : enclosedElements) {
            if (enclosedElement instanceof ExecutableElement) {
                ExecutableElement executableElement = (ExecutableElement) enclosedElement;
                List<? extends AnnotationMirror> annotationMirrors = executableElement.getAnnotationMirrors();
                boolean add = true;
                for (AnnotationMirror annotationMirror : annotationMirrors) {
                    if (annotationMirror.getAnnotationType().toString().equals(Management.class.getName())) {
                        add = false;
                    }
                }
                if (add) {
                    List<ParameterSpec> parameterSpecs = new ArrayList<>();
                    for (VariableElement variableElement : executableElement.getParameters()) {
                        ParameterSpec parameterSpec = ParameterSpec.get(variableElement);
                        List<? extends AnnotationMirror> paramAnnotations = variableElement.getAnnotationMirrors();
                        if (paramAnnotations != null && !paramAnnotations.isEmpty()) {
                            List<AnnotationSpec> annotationSpecs = new ArrayList<>();
                            for (AnnotationMirror annotation : paramAnnotations) {
                                annotationSpecs.add(AnnotationSpec.get(annotation));
                            }
                            parameterSpec = parameterSpec.toBuilder().addAnnotations(annotationSpecs).build();
                        }
                        parameterSpecs.add(parameterSpec);
                    }
                    List<AnnotationSpec> annotationSpecs = new ArrayList<>();
                    for (AnnotationMirror annotationMirror : annotationMirrors) {
                        annotationSpecs.add(AnnotationSpec.get(annotationMirror));
                    }
                    List<TypeName> exceptions = new ArrayList<>();
                    List<? extends TypeMirror> thrownTypes = executableElement.getThrownTypes();
                    for (TypeMirror typeMirror : thrownTypes) {
                        exceptions.add(TypeName.get(typeMirror));
                    }
                    MethodSpec main = MethodSpec.methodBuilder(enclosedElement.getSimpleName().toString())
                            .addModifiers(enclosedElement.getModifiers())
                            .returns(TypeName.get(executableElement.getReturnType()))
                            .addParameters(parameterSpecs)
                            .addAnnotations(annotationSpecs)
                            .addExceptions(exceptions)
                            .build();
                    targetTypeSpec.addMethod(main);
                }
            }
        }
        return GenCodeInfo.builder()
                .className(genClassName)
                .packageName(genPkgName)
                .sourcePath(sourcePath)
                .typeSpec(targetTypeSpec.build())
                .build();
    }
}
