package es.sandwatch.trim;

import org.jetbrains.annotations.NotNull;

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


/**
 * Class containing all report information.
 *
 * @author Ismael Alonso
 * @version 1.0.0
 */
public class Report{
    private List<EndpointReport> endpointReports;


    /**
     * Constructor.
     */
    Report(){
        endpointReports = new ArrayList<>();
    }

    /**
     * Creates an instance of EndpointReport and adds it to the list.
     *
     * @param model the model associated to the report.
     * @param requestResult the result of the request to the above model.
     * @return the report object.
     */
    @NotNull
    EndpointReport addEndpointReport(@NotNull Class<?> model, @NotNull Fetcher.RequestResult requestResult){
        EndpointReport report = new EndpointReport(model, requestResult);
        endpointReports.add(report);
        return report;
    }

    @Override
    public String toString(){
        StringBuilder report = new StringBuilder();
        if (endpointReports.isEmpty()){
            report.append("Nothing to report.");
        }
        else{
            report.append("Trim report, ").append(endpointReports.size()).append(" endpoints:");
            for (EndpointReport endpointReport:endpointReports){
                report.append("\n\n").append(endpointReport);
            }
        }
        return report.toString();
    }


    /**
     * Report for a single model.
     *
     * @author Ismael Alonso
     * @version 1.0.0
     */
    class EndpointReport{
        private Class<?> model;
        private Fetcher.RequestResult requestResult;
        private boolean responseFormatError;
        private List<AttributeReport> attributeReports;


        /**
         * Constructor.
         *
         * @param model the model associated to the report.
         * @param requestResult the result of the request to the above model.
         */
        private EndpointReport(@NotNull Class<?> model, @NotNull Fetcher.RequestResult requestResult){
            this.model = model;
            this.requestResult = requestResult;
            this.responseFormatError = false;
            this.attributeReports = new ArrayList<>();
        }

        /**
         * Lets the report know that the format of the response couldn't be understood.
         */
        void setResponseFormatError(){
            responseFormatError = true;
        }

        /**
         * Adds information about attribute usage to the report.
         *
         * @param attributeReport an attribute report.
         */
        void addAttributeReport(@NotNull AttributeReport attributeReport){
            attributeReports.add(attributeReport);
        }

        @Override
        public String toString(){
            StringBuilder report = new StringBuilder().append(model.toString());
            if (requestResult.requestFailed()){
                report.append("\n  The request could not be performed.");
            }
            else{
                report.append("\n  Request time: ").append(requestResult.getRequestTime()).append("s");
                report.append("\n  Request status code: ").append(requestResult.getStatusCode());
                if (requestResult.is4xx()){
                    report.append("\n  Server response: ").append(requestResult.getResponse());
                }
                else if (responseFormatError){
                    report.append("\n  The format of the response was unknown.");
                }
                else{
                    for (AttributeReport attributeReport:attributeReports){
                        report.append("\n    ").append(attributeReport);
                    }
                }
            }
            return report.toString();
        }
    }


    /**
     * Report for a single attribute.
     *
     * @author Ismael Alonso
     * @version 1.0.0
     */
    static class AttributeReport{
        private String name;
        private boolean used;
        private JsonType apiType;
        private JsonType modelType;


        /**
         * Constructor.
         *
         * @param name the name of the attribute.
         */
        AttributeReport(String name){
            this.name = name;
            this.used = false;
            this.apiType = JsonType.NONE;
            this.modelType = JsonType.NONE;
        }

        /**
         * Sets attribute usage information.
         *
         * @param used true if it is used, false otherwise.
         * @return this object.
         */
        AttributeReport setUsed(boolean used){
            this.used = used;
            return this;
        }

        /**
         * Sets type information.
         *
         * @param apiType the type found in the API endpoint result.
         * @param modelType the type found in the model.
         * @return this object
         */
        AttributeReport setTypes(JsonType apiType, JsonType modelType){
            this.apiType = apiType;
            this.modelType = modelType;
            return this;
        }

        @Override
        public String toString() {
            StringBuilder result = new StringBuilder().append(name).append(": ").append(used ? "used" : "unused");
            if (used){
                result.append(", ");
                if (apiType == modelType){
                    result.append("types match");
                }
                else{
                    result.append("types mismatch (")
                            .append(apiType).append(" in endpoint, ")
                            .append(modelType).append(" in model)");
                }
            }
            return result.toString();
        }
    }
}

@@ -4202,16 +4202,25 @@
 eReport(
+@NotNull 
 String n
@@ -4221,24 +4221,24 @@
 ring name)%7B%0A
-
             
@@ -4927,16 +4927,25 @@
 etTypes(
+@NotNull 
 JsonType
@@ -4953,16 +4953,25 @@
 apiType,
+ @NotNull
  JsonTyp
@@ -5146,17 +5146,16 @@
 String()
- 
 %7B%0A      
@@ -5645,24 +5645,24 @@
 n model)%22);%0A
-
             
@@ -5735,10 +5735,1110 @@
 %7D%0A    %7D%0A
+%0A%0A    /**%0A     * Attribute report for the case where the attribute is an Object or an Array.%0A     *%0A     * @author Ismael Alonso%0A     * @version 1.0.0%0A     */%0A    static class ObjectReport extends AttributeReport%7B%0A        private List%3CAttributeReport%3E attributeReports;%0A%0A%0A        /**%0A         * Constructor.%0A         *%0A         * @param name the name of the attribute.%0A         */%0A        ObjectReport(String name)%7B%0A            super(name);%0A            attributeReports = new ArrayList%3C%3E();%0A        %7D%0A%0A        /**%0A         * Adds an attribute report.%0A         *%0A         * @param attributeReport the attribute report to be added.%0A         */%0A        void addAttributeReport(AttributeReport attributeReport)%7B%0A            attributeReports.add(attributeReport);%0A        %7D%0A%0A        @Override%0A        public String toString()%7B%0A            StringBuilder result = new StringBuilder().append(super.toString());%0A            for (AttributeReport attributeReport:attributeReports)%7B%0A                result.append(%22  %22).append(attributeReport);%0A            %7D%0A            return result.toString();%0A        %7D%0A    %7D%0A
 %7D%0A

Add ObjectReport