package com.cronutils.descriptor.refactor;

import com.cronutils.model.field.expression.*;
import com.cronutils.model.field.expression.visitor.FieldExpressionVisitor;
import com.cronutils.model.field.value.FieldValue;
import com.cronutils.model.field.value.IntegerFieldValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.Validate;

import java.text.MessageFormat;
import java.util.List;
import java.util.ResourceBundle;

class SecondsDescriptor implements FieldExpressionVisitor {
    protected ResourceBundle bundle;

    SecondsDescriptor(ResourceBundle bundle) {
        this.bundle = bundle;
    }

    /**
     * Given a CronFieldExpression, provide a String with a human readable description.
     * Will identify CronFieldExpression subclasses and delegate.
     * @param fieldExpression - CronFieldExpression instance - not null
     * @return human readable description - String
     */
    public String describe(FieldExpression fieldExpression){
        return describe(fieldExpression, false);
    }

    /**
     * Given a CronFieldExpression, provide a String with a human readable description.
     * Will identify CronFieldExpression subclasses and delegate.
     * @param fieldExpression - CronFieldExpression instance - not null
     * @param and - boolean expression that indicates if description should fit an "and" context
     * @return human readable description - String
     */
    protected String describe(FieldExpression fieldExpression, boolean and) {
        Validate.notNull(fieldExpression, "CronFieldExpression should not be null!");
        if (fieldExpression instanceof Always) {
            return describe((Always)fieldExpression, and);
        }
        if (fieldExpression instanceof And) {
            return describe((And) fieldExpression);
        }
        if (fieldExpression instanceof Between) {
            return describe((Between) fieldExpression, and);
        }
        if (fieldExpression instanceof Every) {
            return describe((Every) fieldExpression, and);
        }
        if (fieldExpression instanceof On) {
            return describe((On) fieldExpression, and);
        }
        return "";
    }

    /**
     * Given an int, will return a nominal value. Example:
     * 1 in weeks context, may mean "Monday",
     * so nominal value for 1 would be "Monday"
     * Default will return int as String
     * @param fieldValue - some FieldValue
     * @return String
     */
    protected String nominalValue(FieldValue fieldValue) {
        Validate.notNull(fieldValue, "FieldValue must not be null");
        if(fieldValue instanceof IntegerFieldValue){
            return ""+((IntegerFieldValue)fieldValue).getValue();
        }
        return fieldValue.toString();
    }

    /**
     * Provide a human readable description for Always instance
     * @param always - Always
     * @return human readable description - String
     */
    protected String describe(Always always, boolean and) {
        if (always.getEvery().getTime().getValue() <= 1) {
            return "";
        }
        return describe(always.getEvery(), and);
    }

    /**
     * Provide a human readable description for And instance
     * @param and - And
     * @return human readable description - String
     */
    protected String describe(And and) {
        List<FieldExpression> expressions = Lists.newArrayList();
        List<FieldExpression> onExpressions = Lists.newArrayList();
        for(FieldExpression fieldExpression : and.getExpressions()){
            if(fieldExpression instanceof On){
                onExpressions.add(fieldExpression);
            } else {
                expressions.add(fieldExpression);
            }
        }
        StringBuilder builder = new StringBuilder();
        if(!onExpressions.isEmpty()) {
            builder.append(bundle.getString("at"));
            createAndDescription(builder, onExpressions);
        }
        if(!expressions.isEmpty()){
            createAndDescription(builder, expressions);
        }

        return builder.toString();
    }

    /**
     * Creates human readable description for And element
     * @param builder - StringBuilder instance to which description will be appended
     * @param expressions - field expressions
     * @return same StringBuilder instance as parameter
     */
    @VisibleForTesting
    StringBuilder createAndDescription(StringBuilder builder, List<FieldExpression> expressions){
        if((expressions.size() - 2) >= 0){
            for (int j = 0; j < expressions.size() - 2; j++) {
                builder.append(String.format(" %s, ", describe(expressions.get(j), true)));
            }
            builder.append(String.format(" %s ", describe(expressions.get(expressions.size() - 2), true)));
        }
        builder.append(String.format(" %s ", bundle.getString("and")));
        builder.append(describe(expressions.get(expressions.size()-1), true));
        return builder;
    }

    /**
     * Provide a human readable description for Between instance
     * @param between - Between
     * @return human readable description - String
     */
    protected String describe(Between between, boolean and) {
        return new StringBuilder()
                .append(describe(between.getEvery(), and))
                .append(
                        MessageFormat.format(
                                bundle.getString("between_x_and_y"),
                                nominalValue(between.getFrom()),
                                nominalValue(between.getTo())
                        )
                )
                .append(" ").toString();
    }


    protected String describe(Every every, boolean and) {

    }

    /**
     * Provide a human readable description for On instance
     * @param on - On
     * @return human readable description - String
     */
    protected String describe(On on, boolean and) {
        if(and){
            return nominalValue(on.getTime());
        }
        return String.format("%s %s ", bundle.getString("at"), nominalValue(on.getTime())) + "%s";
    }

    @Override
    public FieldExpression visit(FieldExpression expression) {
        return null;
    }

    @Override
    public Always visit(Always always) {
        return null;
    }

    @Override
    public And visit(And and) {
        return null;
    }

    @Override
    public Between visit(Between between) {
        return null;
    }

    /**
     * Provide a human readable description for Every instance
     * @param every - Every
     * @return human readable description - String
     */
    @Override
    public Every visit(Every every) {
        String description;
        if (every.getTime().getValue() > 1) {
            description = String.format("%s %s ", bundle.getString("every"), nominalValue(every.getTime())) + " %p ";
        } else {
            description = bundle.getString("every")+" %s ";
        }
        //TODO save the description?
        return every;
    }

    @Override
    public On visit(On on) {
        return null;
    }

    @Override
    public QuestionMark visit(QuestionMark questionMark) {
        return null;
    }
}

@@ -5809,16 +5809,29 @@
  and) %7B%0A
+%09return null;
 %0A    %7D%0A%0A

Update SecondsDescriptor fixing return statement.