package ru.yandex.autotests.directintapi.bstransport.matchers.timetarget;

import com.yandex.direct.api.v5.campaigns.TimeTargetingAdd;
import org.apache.commons.lang.ArrayUtils;
import ru.yandex.autotests.directapi.common.api45.TimeTargetInfo;
import ru.yandex.autotests.directapi.common.api45.TimeTargetItem;

import java.util.*;

public class TimeTargetUtils {

    public static boolean targetTimeEquals(List<String> targetTime1, List<String> targetTime2) {
        if (targetTime1 == null && targetTime2 == null) {
            return true;
        }
        if (targetTime1 == null || targetTime2 == null) {
            return false;
        }
        if (targetTime1.size() != targetTime2.size()) {
            return false;
        }
        List<String> targetTimeCopy2 = new ArrayList<>(targetTime2);
        for (String t1 : targetTime1) {
            boolean found = false;
            for (String t2 : targetTimeCopy2) {
                if (t1.equals(t2)) {
                    found = true;
                    targetTimeCopy2.remove(t2);
                    break;
                }
            }
            if (!found) {
                return false;
            }
        }
        return true;
    }

    public static List<String> timeTargetToTransportFormat(TimeTargetInfo timeTargetInfo) {
        if (timeTargetInfo.getShowOnHolidays().equals("No")) {
            return timeTargetToTargetTimeLike(timeTargetInfo);
        }
        if (timeTargetInfo.getShowOnHolidays().equals("Yes") &&
                timeTargetInfo.getHolidayShowFrom() != null &&
                timeTargetInfo.getHolidayShowTo() != null) {
            return timeTargetToTargetTimeLike(timeTargetInfo);
        }
        return timeTargetToTargetTime(timeTargetInfo);
    }


    private static List<String> timeTargetToTargetTime(TimeTargetInfo timeTargetInfo) {
        List<String> targetTimes = new ArrayList<>();
        for (TimeTargetItem timeTargetItem : timeTargetInfo.getDaysHours()) {
            int[] days = invertDays(timeTargetItem.getDays());
            int[] hours = invertHours(timeTargetItem.getHours());
            if (days.length == 0 && hours.length == 0) {
                continue;
            }
            String targetTimeStr = getDaysString(days) + getHoursString(hours);
            targetTimes.add(targetTimeStr);
        }
        return targetTimes.size() > 0 ? targetTimes : null;
    }

    private static List<String> timeTargetToTargetTimeLike(TimeTargetInfo timeTargetInfo) {
        List<String> targetTimes = new ArrayList<>();

        int[] hoursOnHolidays = null;
        if (timeTargetInfo.getHolidayShowFrom() != null && timeTargetInfo.getHolidayShowTo() != null) {
            hoursOnHolidays = getIntsFromPeriod(timeTargetInfo.getHolidayShowFrom(),
                    timeTargetInfo.getHolidayShowTo());
        }

        boolean glue = false;
        for (TimeTargetItem timeTargetItem : timeTargetInfo.getDaysHours()) {
            int[] days = timeTargetItem.getDays();
            int[] hours = timeTargetItem.getHours();
            String targetTimeStr;
            // если часы на выходных совпадают с расписанием определенных дней, то склеиваем
            if (hoursOnHolidays != null && !glue && intsEquals(hoursOnHolidays, hours)) {
                targetTimeStr = getDaysString(days) + "8" + getHoursString(hours);
                glue = true;
            } else {
                targetTimeStr = getDaysString(days) + getHoursString(hours);
            }
            targetTimes.add(targetTimeStr);
        }

        if (hoursOnHolidays != null && !glue) {
            String holidaysTimeStr = "8" + getHoursString(hoursOnHolidays);
            targetTimes.add(holidaysTimeStr);
        }

        return targetTimes.size() > 0 ? targetTimes : null;
    }

    public static List<String> timeTargetingAddToTransportFormat(TimeTargetingAdd timeTargetingAdd) {
        if (timeTargetingAdd.getHolidaysSchedule() == null) {
            return timeTargetingAddToTargetTime(timeTargetingAdd);
        }
        return timeTargetingAddToTargetTimeLike(timeTargetingAdd);
    }

    private static List<String> timeTargetingAddToTargetTime(TimeTargetingAdd timeTargetingAdd) {

        List<String> targetTimes = new ArrayList<>();
        Map<String, ArrayList<Integer>> disabledHours = new HashMap<>();
        List<Integer> daysWithoutDisabledHours = new ArrayList<>();

        for (String itemOfSchedule : getScheduleItemsWithAllDays(timeTargetingAdd.getSchedule().getItems())) {
            int[] itemAsInt = getStringAsNumbers(itemOfSchedule);
            int numberOfDay = itemAsInt[0];
            List<Integer> hoursWithZero = new ArrayList<>();
            for (int i = 1; i < itemAsInt.length; i++) {
                if (itemAsInt[i] == 0) {
                    hoursWithZero.add(i - 1);
                }
            }
            if (hoursWithZero.size() == 0) {
                daysWithoutDisabledHours.add(numberOfDay);
            } else {
                String hoursWithZeroAsString
                        = getHoursString(ArrayUtils.toPrimitive(hoursWithZero.toArray(new Integer[0])));
                addNewDayToMap(disabledHours, hoursWithZeroAsString, numberOfDay);
            }
        }
        for (String hours : disabledHours.keySet()) {
            String targetTimeStr = getDaysString(
                    invertDays(ArrayUtils.toPrimitive(disabledHours.get(hours).toArray(new Integer[0])))
            ) + hours;
            targetTimes.add(targetTimeStr);
        }
        //если есть дни без отключенных часов, то они добавляются в TargetTime отдельной строкой (но не все 7 дней)
        if ((daysWithoutDisabledHours.size() != 0) && (daysWithoutDisabledHours.size() != 7)) {
            targetTimes.add(getDaysString(
                    invertDays(ArrayUtils.toPrimitive(daysWithoutDisabledHours.toArray(new Integer[0])))
            ));
        }
        return targetTimes.size() > 0 ? targetTimes : null;
    }

    private static List<String> timeTargetingAddToTargetTimeLike(TimeTargetingAdd timeTargetingAdd) {

        List<String> targetTimes = new ArrayList<>();
        Map<String, ArrayList<Integer>> enabledHours = new HashMap<>();

        int[] hoursOnHolidays = null;
        if (timeTargetingAdd.getHolidaysSchedule().getStartHour() != null &&
                timeTargetingAdd.getHolidaysSchedule().getEndHour() != null) {
            hoursOnHolidays = getIntsFromPeriod(
                    timeTargetingAdd.getHolidaysSchedule().getStartHour(),
                    timeTargetingAdd.getHolidaysSchedule().getEndHour()
            );
        }

        for (String itemOfSchedule : getScheduleItemsWithAllDays(timeTargetingAdd.getSchedule().getItems())) {
            int[] itemAsInt = getStringAsNumbers(itemOfSchedule);
            int numberOfDay = itemAsInt[0];
            List<Integer> hoursWithPositiveNumbers = new ArrayList<>();
            for (int i = 1; i < itemAsInt.length; i++) {
                if (itemAsInt[i] != 0) {
                    hoursWithPositiveNumbers.add(i - 1);
                }
            }
            if (hoursWithPositiveNumbers.size() != 0) {
                String hoursWithPositiveNumbersAsString =
                        getHoursString(ArrayUtils.toPrimitive(hoursWithPositiveNumbers.toArray(new Integer[0])));
                addNewDayToMap(enabledHours, hoursWithPositiveNumbersAsString, numberOfDay);
            }
        }

        if (hoursOnHolidays != null) {
            addNewDayToMap(enabledHours, getHoursString(hoursOnHolidays), 8);
        }

        for (String hours : enabledHours.keySet()) {
            String targetTimeStr = getDaysString(
                    ArrayUtils.toPrimitive(enabledHours.get(hours).toArray(new Integer[0]))
            ) + hours;
            targetTimes.add(targetTimeStr);
        }
        return targetTimes.size() > 0 ? targetTimes : null;
    }

    // преобразование списка дней и часов в строку

    private static String getDaysString(int[] days) {
        return integersToString(days, DAYS);
    }

    private static String getHoursString(int[] hours) {
        return integersToString(hours, HOURS);
    }

    private static String integersToString(int[] ints, String[] conversion) {
        StringBuilder result = new StringBuilder();
        for (int integer : ints) {
            result.append(conversion[integer]);
        }
        return result.toString();
    }

    // инверсия дней и часов для поля TargetTime

    private static int[] invertDays(int[] days) {
        return invertItems(days, 1, 7);
    }

    private static int[] invertHours(int[] hours) {
        return invertItems(hours, 0, 23);
    }

    private static int[] invertItems(int[] items, int begin, int end) {
        int fullItemsLength = end - begin + 1;
        int[] inverted = new int[fullItemsLength - items.length];
        int index = 0;
        for (int i = begin; i <= end; i++) {
            boolean found = false;
            for (int item : items) {
                if (item == i) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                inverted[index] = i;
                index++;
            }
        }
        return inverted;
    }

    // формат "с - по" -> список часов

    private static int[] getIntsFromPeriod(int from, int to) {
        int[] ints = new int[to - from];
        int cur = from;
        for (int i = 0; i < ints.length; i++) {
            ints[i] = cur++;
        }
        return ints;
    }

    // сравнение упорядоченных массивов целых чисел

    private static boolean intsEquals(int[] items1, int[] items2) {
        if (items1.length != items2.length) {
            return false;
        }
        for (int i = 0; i < items1.length; i++) {
            if (items1[i] != items2[i]) {
                return false;
            }
        }
        return true;
    }

    private static int[] getStringAsNumbers(String inputString) {
        String[] parts = inputString.split(",");
        int[] numbers = new int[parts.length];
        for (int i = 0; i < parts.length; i++) {
            numbers[i] = Integer.parseInt(parts[i]);
        }
        return numbers;
    }

    private static void addNewDayToMap(Map<String, ArrayList<Integer>> disabledHours, String hours, Integer day) {
        if (disabledHours.containsKey(hours)) {
            ArrayList<Integer> valueForModify = disabledHours.get(hours);
            valueForModify.add(day);
            disabledHours.put(hours, valueForModify);
        } else {
            disabledHours.put(hours, new ArrayList<>(Arrays.asList(day)));
        }
    }

    private static List<String> getScheduleItemsWithAllDays(List<String> items) {
        List<String> result = Arrays.asList(
                "1,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100",
                "2,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100",
                "3,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100",
                "4,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100",
                "5,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100",
                "6,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100",
                "7,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100"
        );
        for (String item : items) {
            int numberOfDay = Integer.valueOf(item.substring(0, 1));
            result.set(numberOfDay - 1, item);
        }
        return result;
    }

    // строковое представление дней и часов

    private static final String[] DAYS = new String[]{
            null, "1", "2", "3", "4", "5", "6", "7", "8"};

    private static final String[] HOURS = new String[]{
            "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
            "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
            "U", "V", "W", "X"};
}
