package de.votian.web.view;

import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import de.votian.web.model.SessionData;
import de.votian.web.service.ParameterProvider;
import de.votian.web.service.RestClientService;

import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;

@Route("login")
@PageTitle("Votian - Login")
public class LoginView extends VerticalLayout {

    private final RestClientService restClient;
    private final SessionData sessionData;
    private final ParameterProvider parameterProvider;

    private final TextField accountField = new TextField("Benutzerkonto");
    private final PasswordField passwordField = new PasswordField("Passwort");
    private final IntegerField totpField = new IntegerField("TOTP-Code");
    private final Button loginButton = new Button("Anmelden", event -> submit());

    public LoginView(RestClientService restClient, SessionData sessionData, ParameterProvider parameterProvider) {
        this.restClient = restClient;
        this.sessionData = sessionData;
        this.parameterProvider = parameterProvider;

        if (sessionData.isAuthenticated()) {
            UI.getCurrent().navigate(DashboardView.class);
            return;
        }

        setSizeFull();
        setAlignItems(FlexComponent.Alignment.CENTER);
        setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);

        VerticalLayout loginForm = new VerticalLayout();
        loginForm.setWidth("400px");
        loginForm.setPadding(true);
        loginForm.setSpacing(true);
        loginForm.setAlignItems(FlexComponent.Alignment.CENTER);
        loginForm.getStyle()
                .set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-l)")
                .set("background", "var(--lumo-base-color)")
                .set("box-shadow", "var(--lumo-box-shadow-m)");

        H2 title = new H2("Stadtbote GmbH");
        title.getStyle().set("color", "#1b12b9");

        accountField.setWidthFull();
        accountField.setAutofocus(true);

        passwordField.setWidthFull();

        totpField.setWidthFull();
        totpField.setMin(0);
        totpField.setMax(999999);
        totpField.setVisible(false);

        loginButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        loginButton.setWidthFull();
        loginButton.addClickShortcut(Key.ENTER);

        loginForm.add(title, accountField, passwordField, totpField, loginButton);
        add(loginForm);
    }

    private void submit() {
        if (sessionData.getPendingTotpToken() != null && totpField.isVisible()) {
            verifyTotp();
        } else {
            login();
        }
    }

    private void login() {
        String account = accountField.getValue().trim();
        String password = passwordField.getValue();
        if (account.isEmpty() || password.isEmpty()) {
            showError("Bitte Benutzerkonto und Passwort eingeben.");
            return;
        }

        try {
            Map<String, Object> request = new LinkedHashMap<>();
            request.put("account", account);
            request.put("password", password);

            @SuppressWarnings("unchecked")
            Map<String, Object> response = restClient.post("/auth/login", request, LinkedHashMap.class);
            handleAuthResponse(response);
        } catch (Exception e) {
            showError("Anmeldung fehlgeschlagen. Bitte pruefen Sie Ihre Eingaben.");
        }
    }

    private void verifyTotp() {
        if (totpField.getValue() == null) {
            showError("Bitte den TOTP-Code eingeben.");
            return;
        }

        try {
            Map<String, Object> request = new LinkedHashMap<>();
            request.put("pendingToken", sessionData.getPendingTotpToken());
            request.put("code", String.format("%06d", totpField.getValue()));

            @SuppressWarnings("unchecked")
            Map<String, Object> response = restClient.post("/auth/verify-totp", request, LinkedHashMap.class);
            handleAuthResponse(response);
        } catch (Exception e) {
            showError("TOTP-Pruefung fehlgeschlagen.");
        }
    }

    @SuppressWarnings("unchecked")
    private void handleAuthResponse(Map<String, Object> response) {
        if (response == null) {
            showError("Anmeldung fehlgeschlagen.");
            return;
        }

        if (Boolean.TRUE.equals(response.get("requiresTotp"))) {
            sessionData.setPendingTotpToken((String) response.get("pendingToken"));
            totpField.clear();
            totpField.setVisible(true);
            loginButton.setText("Code pruefen");
            totpField.focus();
            Notification.show("TOTP-Code eingeben.", 3000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_CONTRAST);
            return;
        }

        Map<String, Object> user = (Map<String, Object>) response.get("user");
        if (user == null) {
            showError("Die Anmeldedaten konnten nicht geladen werden.");
            return;
        }

        sessionData.clear();
        sessionData.setAuthToken((String) response.get("authToken"));
        sessionData.setUserId(asLong(user.get("userId")));
        sessionData.setHeadquartersId(asLong(user.get("headquartersId")));
        sessionData.setEmployeeId(asLong(user.get("employeeId")));
        sessionData.setCustomerId(asLong(user.get("customerId")));
        sessionData.setCostCenterId(asLong(user.get("costCenterId")));
        sessionData.setCourierId(asLong(user.get("courierId")));
        sessionData.setUserName(asString(user.get("name"), user.get("userName")));
        sessionData.setUserFirstname(asString(user.get("firstname"), user.get("userFirstname")));
        sessionData.setUserType(user.get("userType") instanceof Number ? ((Number) user.get("userType")).intValue() : 1);
        sessionData.setLegacyRights(asString(user.get("legacyRights"), null));
        sessionData.setAccessibleCostCenterIds(asLongList(user.get("accessibleCostCenterIds")));
        sessionData.setRightIds(asLongList(user.get("rightIds")));
        sessionData.setRightNames(asStringList(user.get("rightNames")));
        sessionData.setAuthenticated(true);

        parameterProvider.init(sessionData.getHeadquartersId(), sessionData.getEmployeeId());
        UI.getCurrent().navigate(DashboardView.class);
    }

    private Long asLong(Object value) {
        return value instanceof Number ? ((Number) value).longValue() : null;
    }

    private List<Long> asLongList(Object value) {
        List<Long> result = new java.util.ArrayList<>();
        if (value instanceof Iterable<?> iterable) {
            for (Object item : iterable) {
                Long parsed = asLong(item);
                if (parsed != null) {
                    result.add(parsed);
                }
            }
        }
        return result;
    }

    private String asString(Object primary, Object fallback) {
        if (primary instanceof String primaryString && !primaryString.isBlank()) {
            return primaryString;
        }
        return fallback instanceof String fallbackString ? fallbackString : null;
    }

    private List<String> asStringList(Object value) {
        List<String> result = new java.util.ArrayList<>();
        if (value instanceof Iterable<?> iterable) {
            for (Object item : iterable) {
                if (item != null) {
                    String text = item.toString();
                    if (!text.isBlank()) {
                        result.add(text);
                    }
                }
            }
        }
        return result;
    }

    private void showError(String message) {
        Notification.show(message, 5000, Notification.Position.MIDDLE)
                .addThemeVariants(NotificationVariant.LUMO_ERROR);
    }
}
