package com.storycove.config;

import com.storycove.service.LibraryService;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.sql.DataSource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Smart routing datasource that:
 * 1. Routes to library-specific databases when a library is active
 * 2. Excludes authentication operations (keeps them on default database)
 * 3. Uses request context to determine when routing is appropriate
 */
public class SmartRoutingDataSource extends AbstractRoutingDataSource {
    
    private static final Logger logger = LoggerFactory.getLogger(SmartRoutingDataSource.class);
    
    private final LibraryService libraryService;
    private final Map<String, DataSource> libraryDataSources = new ConcurrentHashMap<>();
    
    // Database connection details - will be injected via constructor
    private final String baseDbUrl;
    private final String dbUsername; 
    private final String dbPassword;
    
    public SmartRoutingDataSource(LibraryService libraryService, String baseDbUrl, String dbUsername, String dbPassword) {
        this.libraryService = libraryService;
        this.baseDbUrl = baseDbUrl;
        this.dbUsername = dbUsername;
        this.dbPassword = dbPassword;
        
        logger.info("SmartRoutingDataSource initialized with database: {}", baseDbUrl);
    }
    
    @Override
    protected Object determineCurrentLookupKey() {
        try {
            // Check if this is an authentication request - if so, use default database
            if (isAuthenticationRequest()) {
                logger.debug("Authentication request detected, using default database");
                return null; // null means use default datasource
            }
            
            // Check if we have an active library
            if (libraryService != null) {
                String currentLibraryId = libraryService.getCurrentLibraryId();
                if (currentLibraryId != null && !currentLibraryId.trim().isEmpty()) {
                    logger.info("ROUTING: Directing to library-specific database: {}", currentLibraryId);
                    return currentLibraryId;
                } else {
                    logger.info("ROUTING: No active library, using default database");
                }
            } else {
                logger.info("ROUTING: LibraryService is null, using default database");
            }
            
        } catch (Exception e) {
            logger.debug("Error determining lookup key, falling back to default database", e);
        }
        
        return null; // Use default datasource
    }
    
    /**
     * Check if the current request is an authentication request that should use the default database
     */
    private boolean isAuthenticationRequest() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                String requestURI = attributes.getRequest().getRequestURI();
                String method = attributes.getRequest().getMethod();
                
                // Authentication endpoints that should use default database
                if (requestURI.contains("/auth/") || 
                    requestURI.contains("/login") ||
                    requestURI.contains("/api/libraries/switch") ||
                    (requestURI.contains("/api/libraries") && "POST".equals(method))) {
                    return true;
                }
            }
        } catch (Exception e) {
            logger.debug("Could not determine request context", e);
        }
        
        return false;
    }
    
    @Override
    protected DataSource determineTargetDataSource() {
        Object lookupKey = determineCurrentLookupKey();
        
        if (lookupKey != null) {
            String libraryId = (String) lookupKey;
            return getLibraryDataSource(libraryId);
        }
        
        return getDefaultDataSource();
    }
    
    /**
     * Get or create a datasource for the specified library
     */
    private DataSource getLibraryDataSource(String libraryId) {
        return libraryDataSources.computeIfAbsent(libraryId, id -> {
            try {
                HikariConfig config = new HikariConfig();
                
                // Replace database name in URL with library-specific name
                String libraryUrl = baseDbUrl.replaceAll("/[^/]*$", "/" + "storycove_" + id);
                
                config.setJdbcUrl(libraryUrl);
                config.setUsername(dbUsername);
                config.setPassword(dbPassword);
                config.setDriverClassName("org.postgresql.Driver");
                config.setMaximumPoolSize(5); // Smaller pool for library-specific databases
                config.setConnectionTimeout(10000);
                config.setMaxLifetime(600000); // 10 minutes
                
                logger.info("Created new datasource for library: {} -> {}", id, libraryUrl);
                return new HikariDataSource(config);
                
            } catch (Exception e) {
                logger.error("Failed to create datasource for library: {}", id, e);
                return getDefaultDataSource();
            }
        });
    }
    
    private DataSource getDefaultDataSource() {
        // Use the default target datasource that was set in the configuration
        try {
            return (DataSource) super.determineTargetDataSource();
        } catch (Exception e) {
            logger.debug("Could not get default datasource via super method", e);
        }
        
        // Fallback: create a basic datasource
        logger.warn("No default datasource available, creating fallback");
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(baseDbUrl);
        config.setUsername(dbUsername);
        config.setPassword(dbPassword);
        config.setDriverClassName("org.postgresql.Driver");
        config.setMaximumPoolSize(10);
        config.setConnectionTimeout(30000);
        return new HikariDataSource(config);
    }
}