package de.assecutor.votianlt.config;

import com.vaadin.flow.component.UI;
import com.vaadin.flow.server.ServiceInitEvent;
import com.vaadin.flow.server.VaadinServiceInitListener;
import de.assecutor.votianlt.model.Language;
import de.assecutor.votianlt.security.CustomUserPrincipal;
import jakarta.servlet.http.Cookie;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.util.Locale;

/**
 * Sets the user's preferred locale on the UI BEFORE any layout or view is
 * constructed. Registered via {@code UIInitListener} →
 * {@code BeforeEnterListener}, which fires prior to the router creating the
 * layout component tree.
 * 
 * For authenticated users: Uses the language preference from the user profile.
 * For anonymous users: Uses the language from the 'votianlt.language' cookie or
 * falls back to the browser's preferred locale.
 */
@Component
@Slf4j
public class LocaleVaadinInitListener implements VaadinServiceInitListener {

    private static final String LANGUAGE_COOKIE_NAME = "votianlt.language";

    @Override
    public void serviceInit(ServiceInitEvent event) {
        event.getSource().addUIInitListener(uiInitEvent -> {
            UI ui = uiInitEvent.getUI();
            ui.addBeforeEnterListener(beforeEnterEvent -> applyLocale(ui));
        });
    }

    private void applyLocale(UI ui) {
        try {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();

            if (auth != null && auth.isAuthenticated() && !(auth instanceof AnonymousAuthenticationToken)) {
                // Authenticated user: use profile language
                applyLocaleFromAuthenticatedUser(ui, auth);
            } else {
                // Anonymous user: use cookie or browser locale
                applyLocaleFromCookieOrBrowser(ui);
            }
        } catch (Exception e) {
            log.debug("Could not apply locale: {}", e.getMessage());
        }
    }

    private void applyLocaleFromAuthenticatedUser(UI ui, Authentication auth) {
        if (!(auth.getPrincipal() instanceof CustomUserPrincipal cup)) {
            return;
        }
        Language language = cup.getUser().getLanguage();
        if (language == null) {
            return;
        }
        Locale targetLocale = getLocaleFromLanguage(language);
        if (!targetLocale.equals(ui.getLocale())) {
            ui.setLocale(targetLocale);
            log.debug("Locale set to {} for authenticated user {}", targetLocale, cup.getUsername());
        }
    }

    private void applyLocaleFromCookieOrBrowser(UI ui) {
        Locale targetLocale = null;

        // Try to get locale from cookie first
        String cookieLanguage = getLanguageFromCookie(ui);
        if (cookieLanguage != null) {
            targetLocale = getLocaleFromLanguageCode(cookieLanguage);
            log.debug("Using locale {} from cookie for anonymous user", targetLocale);
        }

        // If no cookie, use browser's preferred locale if supported
        if (targetLocale == null) {
            targetLocale = getSupportedLocaleFromBrowser(ui);
            if (targetLocale != null) {
                log.debug("Using browser locale {} for anonymous user", targetLocale);
            }
        }

        // Apply the locale if different from current
        if (targetLocale != null && !targetLocale.equals(ui.getLocale())) {
            ui.setLocale(targetLocale);
            log.debug("Locale set to {} for anonymous user", targetLocale);
        }
    }

    private String getLanguageFromCookie(UI ui) {
        try {
            var request = com.vaadin.flow.server.VaadinRequest.getCurrent();
            if (request == null) {
                return null;
            }

            Cookie[] cookies = request.getCookies();
            if (cookies == null) {
                return null;
            }

            for (Cookie cookie : cookies) {
                if (LANGUAGE_COOKIE_NAME.equals(cookie.getName())) {
                    return cookie.getValue();
                }
            }
        } catch (Exception e) {
            log.debug("Could not read language cookie: {}", e.getMessage());
        }
        return null;
    }

    private Locale getSupportedLocaleFromBrowser(UI ui) {
        // Get the browser's preferred locales
        var locale = ui.getSession().getBrowser().getLocale();
        if (locale == null) {
            return null;
        }

        // Check if the browser locale is supported
        String language = locale.getLanguage().toLowerCase();
        return switch (language) {
        case "de" -> Locale.GERMAN;
        case "en" -> Locale.ENGLISH;
        case "fr" -> Locale.FRENCH;
        case "es" -> Locale.of("es", "ES");
        default -> null; // Return null to use Vaadin's default
        };
    }

    private Locale getLocaleFromLanguage(Language language) {
        return switch (language) {
        case DE -> Locale.GERMAN;
        case EN -> Locale.ENGLISH;
        case FR -> Locale.FRENCH;
        case ES -> Locale.of("es", "ES");
        case TR -> Locale.of("tr", "TR");
        case PL -> Locale.of("pl", "PL");
        case RU -> Locale.of("ru", "RU");
        case EE -> Locale.of("et", "EE");
        case LV -> Locale.of("lv", "LV");
        case LT -> Locale.of("lt", "LT");
        };
    }

    private Locale getLocaleFromLanguageCode(String languageCode) {
        if (languageCode == null) {
            return null;
        }

        return switch (languageCode.toUpperCase()) {
        case "DE" -> Locale.GERMAN;
        case "EN" -> Locale.ENGLISH;
        case "FR" -> Locale.FRENCH;
        case "ES" -> Locale.of("es", "ES");
        case "TR" -> Locale.of("tr", "TR");
        case "PL" -> Locale.of("pl", "PL");
        case "RU" -> Locale.of("ru", "RU");
        case "EE" -> Locale.of("et", "EE");
        case "LV" -> Locale.of("lv", "LV");
        case "LT" -> Locale.of("lt", "LT");
        default -> null;
        };
    }
}
