package com.saas.shared.service;

import com.saas.admin.entity.TenantVoipConfig;
import com.saas.admin.repository.TenantVoipConfigRepository;
import com.saas.shared.dto.VoipConfigDTO;
import com.saas.shared.dto.mapper.VoipConfigDtoMapper;
import com.saas.shared.enums.Provider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

/**
 * Runtime service for resolving VoIP configuration during phone calls.
 * 
 * Responsibilities:
 * - Read-only access to VoIP configs
 * - Database-first lookup with environment variable fallback
 * - Caching for performance
 * - Used by VoIP controllers (TelnyxTeXMLController, TwilioController, etc.)
 * 
 * This service is in the 'shared' module to decouple runtime from admin layer.
 * It follows Clean Architecture by separating runtime queries from admin
 * commands.
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class TenantVoipConfigRuntimeService {

    private final TenantVoipConfigRepository repository;
    private final VoipConfigDtoMapper dtoMapper;

    /**
     * Resolve VoIP configuration by phone number ID (NEW APPROACH).
     * Each phone number can have its own dedicated AI provider config.
     * 
     * Strategy:
     * 1. Check database for active config by phoneNumberId (NO CACHE in DEV mode)
     * 2. If not found, fallback to environment variables (backward compatibility)
     * 3. Return Optional<VoipConfigDTO>
     * 
     * @param phoneNumberId Phone number ID from phone_numbers table
     * @param tenantId      Tenant ID (for fallback to env variables)
     * @param provider      VoIP provider (for fallback to env variables)
     * @return Optional VoIP configuration DTO
     */
    @Cacheable(value = "voipConfigs", key = "#phoneNumberId", unless = "#result == null")
    @Transactional(readOnly = true)
    public Optional<VoipConfigDTO> resolveVoipConfigByPhone(Long phoneNumberId, String tenantId, Provider provider) {
        log.info("🔍 Resolving VoIP config for phoneNumberId: {}, tenant: {}, provider: {}", phoneNumberId, tenantId,
                provider);

        // 1. Try to find active config in database by phone number
        Optional<TenantVoipConfig> dbConfig = repository
                .findByPhoneNumberIdAndIsActive(phoneNumberId, true);

        if (dbConfig.isPresent()) {
            log.info("✅ VoIP config found in database for phoneNumberId: {}", phoneNumberId);
            VoipConfigDTO dto = dtoMapper.toDto(dbConfig.get());
            return Optional.of(dto);
        }

        // 2. Fallback to environment variables (backward compatibility)
        log.info("⚠️ No active config in DB for phoneNumberId: {}, attempting environment variable fallback",
                phoneNumberId);
        return buildConfigFromEnvironment(tenantId, provider);
    }

    /**
     * Resolve VoIP configuration for a tenant and provider (DEPRECATED).
     * 
     * @deprecated Use resolveVoipConfigByPhone instead
     * @param tenantId Tenant ID
     * @param provider VoIP provider (TELNYX, TWILIO, ZIWO)
     * @return Optional VoIP configuration DTO
     */
    @Deprecated
    @Cacheable(value = "voipConfigs", key = "#tenantId + '_' + #provider", unless = "#result == null")
    @Transactional(readOnly = true)
    public Optional<VoipConfigDTO> resolveVoipConfig(String tenantId, Provider provider) {
        log.info("🔍 Resolving VoIP config for tenant: {}, provider: {}", tenantId, provider);

        // 1. Try to find active config in database
        Optional<TenantVoipConfig> dbConfig = repository
                .findByTenantIdAndProviderAndIsActive(tenantId, provider, true);

        if (dbConfig.isPresent()) {
            log.info("✅ VoIP config found in database (active)");
            VoipConfigDTO dto = dtoMapper.toDto(dbConfig.get());
            return Optional.of(dto);
        }

        // 2. Fallback to environment variables
        log.info("⚠️ No active config in DB, attempting environment variable fallback");
        return buildConfigFromEnvironment(tenantId, provider);
    }

    /**
     * Build VoIP configuration from environment variables.
     * This provides backward compatibility for tenants without DB configs.
     * 
     * @param tenantId Tenant ID
     * @param provider VoIP provider
     * @return Optional VoIP configuration from environment
     */
    private Optional<VoipConfigDTO> buildConfigFromEnvironment(String tenantId, Provider provider) {
        if (provider == Provider.TELNYX) {
            return buildTelnyxConfigFromEnv(tenantId);
        } else if (provider == Provider.TWILIO) {
            return buildTwilioConfigFromEnv(tenantId);
        } else if (provider == Provider.ZIWO) {
            return buildZiwoConfigFromEnv(tenantId);
        }

        log.warn("⚠️ No environment variable configuration available for provider: {}", provider);
        return Optional.empty();
    }

    /**
     * Build Telnyx configuration from environment variables
     */
    private Optional<VoipConfigDTO> buildTelnyxConfigFromEnv(String tenantId) {
        String aiType = System.getenv("TELNYX_AI_TYPE");
        String aiAssistantId = System.getenv("TELNYX_AI_ASSISTANT_ID");

        // Support legacy variable name
        if (aiAssistantId == null || aiAssistantId.isEmpty()) {
            aiAssistantId = System.getenv("TELNYX_VOICE_AI_ASSISTANT_ID");
        }

        String streamUrl = System.getenv("TELNYX_STREAM_URL");

        // If no configuration at all, return empty
        if ((aiAssistantId == null || aiAssistantId.isEmpty()) &&
                (streamUrl == null || streamUrl.isEmpty())) {
            log.warn("⚠️ No Telnyx environment variables configured");
            return Optional.empty();
        }

        VoipConfigDTO config = VoipConfigDTO.builder()
                .tenantId(tenantId)
                .provider(Provider.TELNYX)
                .aiAssistantId(aiAssistantId)
                .aiType(aiType != null ? aiType : "TELNYX_NATIVE_AI")
                .streamUrl(streamUrl)
                .isActive(true)
                .fromDatabase(false) // Indicates this comes from .env
                .build();

        log.info("✅ Built Telnyx config from environment variables (fallback mode)");
        return Optional.of(config);
    }

    /**
     * Build Twilio configuration from environment variables
     */
    private Optional<VoipConfigDTO> buildTwilioConfigFromEnv(String tenantId) {
        // Implement Twilio env fallback if needed
        log.warn("⚠️ Twilio environment variable fallback not yet implemented");
        return Optional.empty();
    }

    /**
     * Build Ziwo configuration from environment variables
     */
    private Optional<VoipConfigDTO> buildZiwoConfigFromEnv(String tenantId) {
        // Implement Ziwo env fallback if needed
        log.warn("⚠️ Ziwo environment variable fallback not yet implemented");
        return Optional.empty();
    }

    /**
     * Check if a tenant has an active VoIP configuration.
     * Useful for validation before making calls.
     * 
     * @param tenantId Tenant ID
     * @param provider VoIP provider
     * @return true if valid config exists (DB or env)
     */
    public boolean hasValidConfig(String tenantId, Provider provider) {
        Optional<VoipConfigDTO> config = resolveVoipConfig(tenantId, provider);
        return config.isPresent() && config.get().isValid();
    }
}
