package de.thpeetz.kontor.comics.views;

import java.util.List;

import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
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.checkbox.Checkbox;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.grid.*;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField;

import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import de.thpeetz.kontor.comics.data.Comic;
import de.thpeetz.kontor.comics.data.Issue;
import de.thpeetz.kontor.comics.data.IssueWork;
import de.thpeetz.kontor.comics.data.Volume;
import de.thpeetz.kontor.common.views.YearMonthField;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class IssueForm extends FormLayout {

    ComboBox<Comic> comic = new ComboBox<>("Comic");
    ComboBox<Volume> volume = new ComboBox<>("Volume");
    TextField issueNumber = new TextField("Issue number");
    TextField title = new TextField("Full Title");
    YearMonthField publishedOn = new YearMonthField();
    Checkbox isRead = new Checkbox("Read");
    Checkbox inStock = new Checkbox("In stock");
    Grid<IssueWork> issueWorks = new Grid<>(IssueWork.class);

    Button save = new Button("Save");
    Button delete = new Button("Delete");
    Button close = new Button("Cancel");

    Binder<Issue> binder = new BeanValidationBinder<>(Issue.class);

    public IssueForm(List<Comic> comics, List<Volume> volumes) {
        addClassName("issue-form");
        binder.bindInstanceFields(this);

        comic.setItems(comics);
        comic.addValueChangeListener(event -> {
            if (event.getValue() != null) {
                volume.setItems(event.getValue().getVolumes());
            } else {
                volume.clear();
            }
        });
        comic.setItemLabelGenerator(Comic::getTitle);
        volume.setItems(volumes);
        volume.setItemLabelGenerator(Volume::getName);
        issueWorks.setColumns("workType.name", "artist.name");
        issueWorks.getColumnByKey("workType.name").setHeader("Work type");
        issueWorks.getColumnByKey("artist.name").setHeader("Artist");
        issueWorks.getColumns().forEach(col -> col.setAutoWidth(true));
        add(comic, volume, issueNumber, title, publishedOn, isRead, inStock,issueWorks, createButtonsLayout());
    }

    private HorizontalLayout createButtonsLayout() {
        save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
        close.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

        save.addClickShortcut(Key.ENTER);
        close.addClickShortcut(Key.ESCAPE);

        save.addClickListener(event -> validateAndSave());
        delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean())));
        close.addClickListener(event -> fireEvent(new CloseEvent(this)));

        binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));
        return new HorizontalLayout(save, delete, close);
    }

    private void validateAndSave() {
        if (binder.isValid()) {
            fireEvent(new SaveEvent(this, binder.getBean()));
        }
    }

    public void setIssue(Issue issue) {
        log.debug("IssueForm.setIssue: {}", issue);
        Volume issueVolume = null;
        if (issue != null) {
            issueVolume = issue.getVolume();
        }
        binder.setBean(issue);
        if (issue != null && issue.getComic() != null) {
            log.debug("populate list of volumes");
            volume.setItems(issue.getComic().getVolumes());
            log.debug("Issue.Volume= {}", issue.getVolume());
            if (issueVolume != null) {
                log.debug("Set volume {} for issue: {}", issueVolume, issue);
                volume.setValue(issueVolume);
            }
        }
    }

    public void setIssueWorks(List<IssueWork> works) {
        log.info("Setting issue works: {}", works);
        issueWorks.setItems(works);
    }

    public abstract static class IssueFormEvent extends ComponentEvent<IssueForm> {
        @lombok.Getter
        private final Issue issue;

        protected IssueFormEvent(IssueForm source, Issue issue) {
            super(source, false);
            this.issue = issue;
        }
    }

    public static class SaveEvent extends IssueFormEvent {
        SaveEvent(IssueForm source, Issue issue) {
            super(source, issue);
        }
    }

    public static class DeleteEvent extends IssueFormEvent {
        DeleteEvent(IssueForm source, Issue issue) {
            super(source, issue);
        }
    }

    public static class CloseEvent extends IssueFormEvent {
        CloseEvent(IssueForm source) {
            super(source, null);
        }
    }

    public void addDeleteListener(ComponentEventListener<DeleteEvent> listener) {
        addListener(DeleteEvent.class, listener);
    }

    public void addSaveListener(ComponentEventListener<SaveEvent> listener) {
        addListener(SaveEvent.class, listener);
    }

    public void addCloseListener(ComponentEventListener<CloseEvent> listener) {
        addListener(CloseEvent.class, listener);
    }
}
