package ru.vyatsu.qr_access_admin.door.component;

import com.vaadin.flow.component.Composite;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import lombok.Getter;
import lombok.Setter;
import ru.vyatsu.qr_access_admin.door.entity.DoorEntity;
import ru.vyatsu.qr_access_admin.door.entity.DoorRepository;
import ru.vyatsu.qr_access_admin.door.schedule.component.ScheduleEditor;
import ru.vyatsu.qr_access_admin.door.schedule.entity.ScheduleEntity;
import ru.vyatsu.qr_access_admin.door.schedule.entity.ScheduleRepository;
import ru.vyatsu.qr_access_admin.unit.entity.UnitRepository;
import ru.vyatsu.qr_access_admin.unit.mapper.UnitEntityUnitComboBoxModelMapper;
import ru.vyatsu.qr_access_admin.unit.model.UnitComboBoxModel;

import java.util.*;
import java.util.stream.Collectors;

public class DoorEditor extends Composite<HorizontalLayout> {

    public interface SaveListener {
        void onSave(DoorEditInfo editInfo);
    }

    public interface DeleteListener {
        void onDelete(DoorEntity door) throws Exception;
    }

    public interface CancelListener {
        void onCancel();
    }

    private DoorEntity currentDoor;

    private final ScheduleEditor scheduleEditor;

    private final VerticalLayout additionalDoorsEditor = new VerticalLayout();

    private final Map<String, DoorEntity> additionalDoors = new HashMap<>();
    private final Map<String, DoorEntity> additionalDoorsWasLinked = new HashMap<>();
    private final Collection<DoorEntity> createdAdditionalDoors = new ArrayList<>();

    private final DoorRepository repository;

    @Getter
    @Setter
    private SaveListener saveListener;
    @Getter
    @Setter
    private DeleteListener deleteListener;
    @Getter
    @Setter
    private CancelListener cancelListener;

    private final Binder<DoorEntity> binder = new BeanValidationBinder<>(DoorEntity.class);

    public void setCurrentDoor(DoorEntity door) {
        this.currentDoor = door;
        binder.setBean(door);
        scheduleEditor.setCurrentDoorId(door.getId());
        scheduleEditor.refreshScheduleView();
        this.refreshAdditionalDoors();
    }

    private void refreshAdditionalDoors() {
        additionalDoorsEditor.removeAll();
        additionalDoors.clear();
        createdAdditionalDoors.clear();
        List<DoorEntity> existAdditionalDoors = repository.findAllByParentDoorIdsIsNotNull();
        existAdditionalDoors
                .stream()
                .filter(ad -> currentDoor != null && new HashSet<>(Set.of(ad.getParentDoorIds().split(","))).contains(currentDoor.getId()))
                .forEach(ad -> {
                    HorizontalLayout choose = new HorizontalLayout();
                    ComboBox<DoorEntity> comboBox = new ComboBox<>("Выбрать из имеющихся");
                    comboBox.setItems(existAdditionalDoors);
                    comboBox.setItemLabelGenerator(DoorEntity::getDescription);
                    comboBox.setValue(ad);
                    comboBox.setAllowCustomValue(true);
                    comboBox.addValueChangeListener(e -> {
                        DoorEntity chosenValue = e.getValue();
                        DoorEntity oldValue = e.getOldValue();
                        if (oldValue != null) {
                            additionalDoors.remove(oldValue.getId());
                        }
                        if (chosenValue != null) {
                            additionalDoors.put(chosenValue.getId(), chosenValue);
                        }
                    });
                    comboBox.addCustomValueSetListener(e -> {
                        DoorEntity newDoor = new DoorEntity();
                        newDoor.setCount(0);
                        newDoor.setDescription(e.getDetail());
                        newDoor.setUnitId(currentDoor.getUnitId());
                        createdAdditionalDoors.add(newDoor);
                        existAdditionalDoors.add(newDoor);
                        comboBox.setItems(existAdditionalDoors);
                        comboBox.setValue(newDoor);
                    });
                    var deleteAdditionalDoor = new Button(VaadinIcon.TRASH.create());
                    additionalDoors.put(ad.getId(), ad);
                    additionalDoorsWasLinked.put(ad.getId(), ad);
                    deleteAdditionalDoor.addClickListener(ev -> {
                        additionalDoorsEditor.remove(choose);
                        additionalDoors.remove(ad.getId());
                    });
                    choose.add(comboBox, deleteAdditionalDoor);
                    additionalDoorsEditor.add(choose);
                });
        var addAdditionalDoor = new Button("Привязать дверь", VaadinIcon.PLUS.create());
        addAdditionalDoor.addClickListener(e -> {
            HorizontalLayout choose = new HorizontalLayout();
            ComboBox<DoorEntity> comboBox = new ComboBox<>("Выбрать из имеющихся");
            comboBox.setItems(existAdditionalDoors);
            comboBox.setItemLabelGenerator(DoorEntity::getDescription);
            comboBox.setAllowCustomValue(true);
            comboBox.addValueChangeListener(ce -> {
                DoorEntity chosenValue = ce.getValue();
                DoorEntity oldValue = ce.getOldValue();
                if (oldValue != null) {
                    additionalDoors.remove(oldValue.getId());
                }
                if (chosenValue != null) {
                    additionalDoors.put(chosenValue.getId(), chosenValue);
                }
            });
            comboBox.addCustomValueSetListener(ce -> {
                DoorEntity newDoor = new DoorEntity();
                newDoor.setCount(0);
                newDoor.setDescription(ce.getDetail());
                newDoor.setUnitId(currentDoor.getUnitId());
                createdAdditionalDoors.add(newDoor);
                existAdditionalDoors.add(newDoor);
                comboBox.setItems(existAdditionalDoors);
                comboBox.setValue(newDoor);
            });
            var deleteAdditionalDoor = new Button(VaadinIcon.TRASH.create());
            deleteAdditionalDoor.addClickListener(ev -> {
                additionalDoorsEditor.remove(choose);
                if (comboBox.getValue() != null) {
                    additionalDoors.remove(comboBox.getValue().getId());
                }
            });
            choose.add(comboBox, deleteAdditionalDoor);
            additionalDoorsEditor.addComponentAtIndex(additionalDoorsEditor.getComponentCount() - 1, choose);
        });
        additionalDoorsEditor.add(addAdditionalDoor);
    }

    public DoorEditor(UnitRepository unitRepository, ScheduleRepository scheduleRepository, DoorRepository repository) {
        this.repository = repository;
        ComboBox<UnitComboBoxModel> unitField = new ComboBox<>("Устройство");
        unitField.setRequired(true);
        unitField.setPageSize(100);
        Map<String, UnitComboBoxModel> units = unitRepository.findAllByAdminEditableIsTrue()
                .stream()
                .map(new UnitEntityUnitComboBoxModelMapper()::toModel)
                .collect(Collectors.toMap(UnitComboBoxModel::clientId, unit -> unit));
        unitField.setItems(units.values());
        unitField.setItemLabelGenerator(UnitComboBoxModel::clientName);
        IntegerField countField = new IntegerField("Количество мест");
        TextField descriptionField = new TextField("Описание");


        var save = new Button("Сохранить", VaadinIcon.CHECK.create());
        var cancel = new Button("Отмена");
        var delete = new Button("Удалить", VaadinIcon.TRASH.create());

        binder.forField(unitField)
                .withConverter(UnitComboBoxModel::clientId, units::get, "Invalid value")
                .bind("unitId");
        binder.forField(countField).bind("count");
        binder.forField(descriptionField).bind("description");

        save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        scheduleEditor = new ScheduleEditor(scheduleRepository);

        save.addClickListener(e -> saveListener.onSave(new DoorEditInfo(currentDoor, scheduleEditor.getNewSchedule(), additionalDoors, additionalDoorsWasLinked)));
        save.addClickShortcut(Key.ENTER);

        delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
        delete.addClickListener(e -> {
            try {
                deleteListener.onDelete(currentDoor);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        });

        this.refreshAdditionalDoors();

        cancel.addClickListener(e -> cancelListener.onCancel());

        VerticalLayout mainForm = new VerticalLayout();
        mainForm.add(unitField, countField, descriptionField, scheduleEditor, new HorizontalLayout(save, cancel, delete));

        getContent().add(mainForm, additionalDoorsEditor);
    }

    public record DoorEditInfo(DoorEntity mainDoor, Collection<ScheduleEntity> schedule,
                               Map<String, DoorEntity> additionalDoorsToSave,
                               Map<String, DoorEntity> additionalDoorsWasLinked) {

    }
}
