package de.votian.web.service;

import com.vaadin.flow.spring.annotation.VaadinSessionScope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Session-scoped parameter provider for the Vaadin web application.
 * <p>
 * Equivalent to PHP's defineGlobalParameters() which loads all parameters
 * for the current headquarters and makes them available as constants.
 * <p>
 * Parameters are loaded once per session (at login) and cached.
 * Individual parameters can also be fetched on-demand via the REST API
 * with full PHP-compatible fallback logic.
 * <p>
 * Usage in views:
 * <pre>
 *   &#64;Autowired
 *   private ParameterProvider parameterProvider;
 *
 *   String value = parameterProvider.get("MASK_MULTI_JOBLIST");
 *   boolean enabled = parameterProvider.is("CS_TRACKING_ENABLED");
 *   int count = parameterProvider.getInt("MASK_MULTI_JOBLIST", 4);
 * </pre>
 */
@Component
@VaadinSessionScope
public class ParameterProvider {

    private static final Logger log = LoggerFactory.getLogger(ParameterProvider.class);

    private final RestClientService restClient;

    /** Cached parameters: key -> value */
    private final Map<String, String> cache = new ConcurrentHashMap<>();

    private boolean loaded = false;
    private Long headquartersId;
    private Long employeeId;

    public ParameterProvider(RestClientService restClient) {
        this.restClient = restClient;
    }

    /**
     * Initialize the parameter cache for the current session.
     * Should be called after successful login.
     *
     * @param hqId  Headquarters ID of the logged-in user
     * @param empId Employee ID of the logged-in user
     */
    public void init(Long hqId, Long empId) {
        this.headquartersId = hqId;
        this.employeeId = empId != null ? empId : 0L;
        reload();
    }

    /**
     * Reload all parameters from the backend.
     * Calls the /api/parameters/bulk endpoint which returns all global + employee-specific parameters.
     */
    public void reload() {
        if (headquartersId == null) {
            log.warn("Cannot load parameters: headquartersId not set. Call init() first.");
            return;
        }

        try {
            Map<String, Object> params = restClient.getMap(
                    "/parameters/bulk?hqId=" + headquartersId + "&empId=" + employeeId
            );
            cache.clear();
            if (params != null) {
                for (Map.Entry<String, Object> entry : params.entrySet()) {
                    cache.put(entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : "");
                }
            }
            loaded = true;
            log.info("Loaded {} parameters for hq={}, emp={}", cache.size(), headquartersId, employeeId);
        } catch (Exception e) {
            log.error("Failed to load parameters for hq={}, emp={}", headquartersId, employeeId, e);
        }
    }

    // ========================================================================
    // Parameter access methods
    // ========================================================================

    /**
     * Get a parameter value from the cache.
     *
     * @param key Parameter key (use constants from ParameterKeys)
     * @return Parameter value or empty string if not found
     */
    public String get(String key) {
        return cache.getOrDefault(key, "");
    }

    /**
     * Get a parameter value with a default fallback.
     */
    public String get(String key, String defaultValue) {
        String value = cache.get(key);
        return (value != null && !value.isEmpty()) ? value : defaultValue;
    }

    /**
     * Get a parameter value as integer.
     */
    public int getInt(String key, int defaultValue) {
        String value = cache.get(key);
        if (value != null && !value.isEmpty()) {
            try {
                return Integer.parseInt(value.trim());
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;
    }

    /**
     * Get a parameter value as long.
     */
    public long getLong(String key, long defaultValue) {
        String value = cache.get(key);
        if (value != null && !value.isEmpty()) {
            try {
                return Long.parseLong(value.trim());
            } catch (NumberFormatException e) {
                return defaultValue;
            }
        }
        return defaultValue;
    }

    /**
     * Check if a parameter is enabled (value is "1").
     * This is the most common boolean check in the PHP code.
     */
    public boolean is(String key) {
        return "1".equals(cache.get(key));
    }

    /**
     * Check if a parameter has a non-empty value.
     */
    public boolean has(String key) {
        String value = cache.get(key);
        return value != null && !value.isEmpty();
    }

    /**
     * Get an object-based parameter (dynamic key with object ID suffix).
     * First checks key_objId, then falls back to the base key.
     *
     * @param baseKey Base parameter key (e.g., "CUSTOMER_MASK_JOBLIST_FIELDS")
     * @param objId   Object ID (e.g., customer ID)
     * @return Parameter value or empty string
     */
    public String getForObject(String baseKey, Long objId) {
        if (objId != null && objId > 0) {
            String objValue = cache.get(baseKey + objId);
            if (objValue != null && !objValue.isEmpty()) {
                return objValue;
            }
            // Also try with underscore separator (KEY_123456)
            if (!baseKey.endsWith("_")) {
                objValue = cache.get(baseKey + "_" + objId);
                if (objValue != null && !objValue.isEmpty()) {
                    return objValue;
                }
            }
        }
        // Fallback to base key
        return get(baseKey);
    }

    /**
     * Check if an object-based parameter is enabled.
     */
    public boolean isForObject(String baseKey, Long objId) {
        return "1".equals(getForObject(baseKey, objId));
    }

    // ========================================================================
    // Fetch individual parameters via REST (bypassing cache)
    // ========================================================================

    /**
     * Fetch a parameter value directly from the backend with full fallback logic.
     * Use this for parameters that may not be in the bulk-loaded cache
     * (e.g., employee-specific parameters for other employees).
     */
    public String fetchValue(String key, Long hqId, Long empId) {
        try {
            String url = "/parameters/value-full-fallback?key=" + key
                    + "&hqId=" + hqId + "&empId=" + (empId != null ? empId : 0);
            String value = restClient.get(url, String.class);
            return value != null ? value : "";
        } catch (Exception e) {
            log.debug("Parameter not found via REST: key={}, hq={}, emp={}", key, hqId, empId);
            return "";
        }
    }

    /**
     * Fetch a parameter with the current session's headquarters.
     */
    public String fetchValue(String key) {
        return fetchValue(key, headquartersId, employeeId);
    }

    // ========================================================================
    // State
    // ========================================================================

    public boolean isLoaded() {
        return loaded;
    }

    public int size() {
        return cache.size();
    }

    public Long getHeadquartersId() {
        return headquartersId;
    }

    public Long getEmployeeId() {
        return employeeId;
    }

    /**
     * Get all cached parameters as an unmodifiable map.
     */
    public Map<String, String> getAll() {
        return Collections.unmodifiableMap(cache);
    }
}
