package de.assecutor.votianlt.pages.view;

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.Div;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.login.LoginForm;
import com.vaadin.flow.component.login.LoginI18n;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.AfterNavigationEvent;
import com.vaadin.flow.router.AfterNavigationObserver;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.router.HasDynamicTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.auth.AnonymousAllowed;
import de.assecutor.votianlt.security.totp.TwoFactorService;
import de.assecutor.votianlt.repository.UserRepository;
import de.assecutor.votianlt.model.User;
import de.assecutor.votianlt.security.SessionAuthenticationService;
import de.assecutor.votianlt.service.DemoModeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import jakarta.annotation.PostConstruct;

@Route("login")
@AnonymousAllowed
public class LoginView extends VerticalLayout implements BeforeEnterObserver, AfterNavigationObserver, HasDynamicTitle {

    private final LoginForm loginForm = new LoginForm();
    private final TextField twoFaField = new TextField(getTranslation("login.2fa.title"));
    private final Button verify2faButton = new Button(getTranslation("login.2fa.button"));
    private final Div flashBox = new Div();

    @Autowired
    private TwoFactorService twoFactorService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private Environment environment;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private SessionAuthenticationService sessionAuthenticationService;

    @Value("${app.security.two-factor.enabled:false}")
    private boolean twoFactorEnabledGlobal;

    @Value("${app.version:unknown}")
    private String appVersion;

    private Authentication pendingAuth;
    private Span versionSpan;

    public LoginView() {
        addClassName("login-view");
        setSizeFull();

        setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
        setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);

        // Eigene Authentifizierung: Kein POST auf /login
        loginForm.setAction(null);

        // LoginForm I18n konfigurieren
        LoginI18n i18n = LoginI18n.createDefault();
        LoginI18n.Form form = i18n.getForm();
        form.setTitle(getTranslation("login.title"));
        form.setUsername(getTranslation("login.username"));
        form.setPassword(getTranslation("login.password"));
        form.setSubmit(getTranslation("login.login"));
        form.setForgotPassword(getTranslation("login.forgotpassword"));
        i18n.setForm(form);
        loginForm.setI18n(i18n);

        twoFaField.setVisible(false);
        twoFaField.setMaxLength(6);
        twoFaField.setPattern("[0-9]{6}");
        twoFaField.setHelperText(getTranslation("login.2fa.helper"));
        verify2faButton.setVisible(false);
        verify2faButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        verify2faButton.addClickListener(e -> handleVerify2fa());
        // Enable built-in forgot password link and navigate to request view
        loginForm.setForgotPasswordButtonVisible(true);
        loginForm.addForgotPasswordListener(e -> UI.getCurrent().navigate(ForgotPasswordRequestView.class));

        Button registerButton = new Button(getTranslation("login.register"), e -> UI.getCurrent().navigate("register"));
        registerButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
        registerButton.addClassName("login-register-button");

        // Version display - will be set in @PostConstruct
        versionSpan = new Span("");
        versionSpan.addClassName("login-version");

        // Inline flash message box (hidden by default)
        flashBox.addClassName("inline-flash");
        flashBox.getStyle().set("display", "none");

        VerticalLayout loginLayout = new VerticalLayout();
        loginLayout.setAlignItems(FlexComponent.Alignment.CENTER);
        loginLayout.addClassName("login-card");

        loginLayout.add(flashBox, loginForm, twoFaField, verify2faButton, registerButton, versionSpan);
        loginLayout.setPadding(true);

        Div loginShell = new Div(loginLayout);
        loginShell.addClassName("login-shell");

        add(loginShell);
        // Login-Schritt 1: Benutzername/Passwort
        loginForm.addLoginListener(e -> handlePasswordLogin(e.getUsername(), e.getPassword()));
    }

    @PostConstruct
    private void init() {
        // Set version after Spring injection
        String[] activeProfiles = environment.getActiveProfiles();
        String[] defaultProfiles = environment.getDefaultProfiles();

        String profileInfo = "";
        if (activeProfiles.length > 0) {
            profileInfo = " (" + String.join(", ", activeProfiles) + ")";
        } else if (defaultProfiles.length > 0) {
            profileInfo = " (" + String.join(", ", defaultProfiles) + ")";
        } else {
            profileInfo = " (default)";
        }

        versionSpan.setText("Version " + appVersion + profileInfo);
    }

    private void handlePasswordLogin(String username, String password) {
        hideInlineFlash();

        if (DemoModeService.isDemoUsername(username)) {
            pendingAuth = null;
            loginForm.setError(true);
            showInlineFlash(getTranslation("login.demo.only.button"));
            return;
        }

        try {
            // Prüfe Benutzername/Passwort
            Authentication auth = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));

            // Prüfe ob 2FA für diesen Nutzer aktiviert ist (global UND nutzer-spezifisch)
            boolean userTwoFactorEnabled = userRepository.findByEmail(username).map(User::isTwoFactorEnabled)
                    .orElse(true); // Standardmäßig aktiviert falls Nutzer nicht gefunden

            if (twoFactorEnabledGlobal && userTwoFactorEnabled) {
                // 2FA aktiviert: Benutzer noch nicht in SecurityContext setzen
                this.pendingAuth = auth;
                twoFaField.setVisible(true);
                verify2faButton.setVisible(true);
                twoFactorService.initiateTwoFactorFor(username);
                Notification.show(getTranslation("login.2fa.sent"), 3000, Notification.Position.BOTTOM_CENTER);
            } else {
                // 2FA deaktiviert: Direkt anmelden
                sessionAuthenticationService.storeAuthentication(auth);
                // Check if user is admin and redirect accordingly
                if (auth.getAuthorities().stream()
                        .anyMatch(authority -> authority.getAuthority().equals("ROLE_ADMIN"))) {
                    UI.getCurrent().getPage().setLocation("/admin-dashboard");
                } else {
                    UI.getCurrent().getPage().setLocation("/dashboard");
                }
            }
        } catch (Exception ex) {
            loginForm.setError(true);
            this.pendingAuth = null;
        }
    }

    private void handleVerify2fa() {
        if (pendingAuth == null) {
            Notification.show(getTranslation("login.2fa.no.credentials"), 3000, Notification.Position.BOTTOM_CENTER);
            return;
        }
        String username = pendingAuth.getName();
        String code = twoFaField.getValue();
        if (code == null || !code.matches("[0-9]{6}")) {
            Notification.show(getTranslation("login.2fa.invalid.code"), 3000, Notification.Position.BOTTOM_CENTER);
            return;
        }
        boolean ok = twoFactorService.verifyTwoFactorCode(username, code);
        if (!ok) {
            Notification.show(getTranslation("login.2fa.wrong.code"), 3000, Notification.Position.BOTTOM_CENTER);
            return;
        }
        // 2FA korrekt: Benutzer nun anmelden
        Authentication authenticatedUser = pendingAuth;
        sessionAuthenticationService.storeAuthentication(authenticatedUser);
        this.pendingAuth = null;
        // Full reload, damit der neue SecurityContext im UI sicher greift
        // Check if user is admin and redirect accordingly
        if (authenticatedUser.getAuthorities().stream()
                .anyMatch(auth -> auth.getAuthority().equals("ROLE_ADMIN"))) {
            UI.getCurrent().getPage().setLocation("/admin-dashboard");
        } else {
            UI.getCurrent().getPage().setLocation("/dashboard");
        }
    }

    @Override
    public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
        // Zeige Fehlermeldung bei fehlgeschlagener Anmeldung
        if (beforeEnterEvent.getLocation().getQueryParameters().getParameters().containsKey("error")) {
            loginForm.setError(true);
        }
    }

    @Override
    public void afterNavigation(AfterNavigationEvent event) {
        // Show flash message after the view is attached and rendered
        var session = com.vaadin.flow.server.VaadinSession.getCurrent();
        if (session != null) {
            Object msg = session.getAttribute("flashMessage");
            if (msg instanceof String && !((String) msg).isBlank()) {
                session.setAttribute("flashMessage", null); // clear
                // Inline message for guaranteed visibility
                flashBox.setText((String) msg);
                flashBox.getStyle().set("display", "block");
            } else {
                flashBox.setText("");
                flashBox.getStyle().set("display", "none");
            }
        }
    }

    @Override
    public String getPageTitle() {
        return getTranslation("page.title.login");
    }

    private void showInlineFlash(String message) {
        flashBox.setText(message);
        flashBox.getStyle().set("display", "block");
    }

    private void hideInlineFlash() {
        flashBox.setText("");
        flashBox.getStyle().set("display", "none");
    }
}
