package com.social.media.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.social.media.dto.SocialNetworkDto;
import com.social.media.domain.entity.SocialNetwork;
import com.social.media.repository.SocialNetworkRepository;
import com.social.media.service.SocialNetworkService;
import com.social.media.util.SocialNetworkMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Implementation of SocialNetworkService
 */
@Service
@Transactional
@RequiredArgsConstructor
@Slf4j
public class SocialNetworkServiceImpl implements SocialNetworkService {
    
    private final SocialNetworkRepository repository;
    
    @Override
    public SocialNetworkDto create(SocialNetworkDto dto) {
        log.info("Creating new social network: {}", dto.getNetworkCode());
        
        // Check if network code already exists
        if (repository.existsByNetworkCode(dto.getNetworkCode())) {
            throw new IllegalArgumentException("Social network with code '" + dto.getNetworkCode() + "' already exists");
        }
        
        SocialNetwork entity = SocialNetworkMapper.toEntity(dto);
        entity.setCreatedAt(LocalDateTime.now());
        entity.setUpdatedAt(LocalDateTime.now());
        
        SocialNetwork saved = repository.save(entity);
        log.info("Successfully created social network with ID: {}", saved.getId());
        
        return SocialNetworkMapper.toDto(saved);
    }
    
    @Override
    public SocialNetworkDto update(Long id, SocialNetworkDto dto) {
        log.info("Updating social network with ID: {}", id);
        
        SocialNetwork existing = repository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Social network not found with ID: " + id));
        
        // Don't allow changing network code
        if (!existing.getNetworkCode().equals(dto.getNetworkCode())) {
            throw new IllegalArgumentException("Network code cannot be changed after creation");
        }
        
        SocialNetworkMapper.updateEntityFromDto(existing, dto);
        existing.setUpdatedAt(LocalDateTime.now());
        
        SocialNetwork saved = repository.save(existing);
        log.info("Successfully updated social network: {}", saved.getNetworkCode());
        
        return SocialNetworkMapper.toDto(saved);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Optional<SocialNetworkDto> findById(Long id) {
        return repository.findById(id)
                .map(SocialNetworkMapper::toDto);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Optional<SocialNetworkDto> findByNetworkCode(String networkCode) {
        return repository.findByNetworkCode(networkCode)
                .map(SocialNetworkMapper::toDto);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<SocialNetworkDto> findAll(Pageable pageable) {
        Page<SocialNetwork> entities = repository.findAll(pageable);
        return entities.map(SocialNetworkMapper::toDto);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialNetworkDto> findAllActive() {
        return repository.findByIsActiveTrueOrderBySortOrderAscNameAsc()
                .stream()
                .map(SocialNetworkMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public Page<SocialNetworkDto> findAllActive(Pageable pageable) {
        Page<SocialNetwork> entities = repository.findByIsActiveTrueOrderBySortOrderAscNameAsc(pageable);
        return entities.map(SocialNetworkMapper::toDto);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialNetworkDto> findByPlatform(String platform) {
        return repository.findByPlatformOrderBySortOrderAscNameAsc(platform)
                .stream()
                .map(SocialNetworkMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialNetworkDto> findActiveByPlatform(String platform) {
        return repository.findByPlatformAndIsActiveTrueOrderBySortOrderAscNameAsc(platform)
                .stream()
                .map(SocialNetworkMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialNetworkDto> findNetworksWithFeature(String feature) {
        return repository.findNetworksSupportingFeature(feature)
                .stream()
                .map(SocialNetworkMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialNetworkDto> findActiveNetworksWithFeature(String feature) {
        return repository.findNetworksSupportingFeature(feature)
                .stream()
                .map(SocialNetworkMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialNetworkDto> findNetworksRequiringApproval() {
        return repository.findByRequiresApprovalTrueOrderBySortOrderAscNameAsc(Pageable.unpaged())
                .getContent()
                .stream()
                .map(SocialNetworkMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialNetworkDto> findProductionReadyNetworks() {
        return repository.findProductionReadyNetworks()
                .stream()
                .map(SocialNetworkMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialNetworkDto> findByBrandColor(String color) {
        return repository.findByBrandColorOrderBySortOrderAscNameAsc(color)
                .stream()
                .map(SocialNetworkMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialNetworkDto> searchByName(String name) {
        return repository.searchByNameOrPlatform(name, Pageable.unpaged())
                .getContent()
                .stream()
                .map(SocialNetworkMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    public void activate(Long id) {
        log.info("Activating social network with ID: {}", id);
        SocialNetwork entity = repository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Social network not found with ID: " + id));
        
        entity.setIsActive(true);
        entity.setUpdatedAt(LocalDateTime.now());
        repository.save(entity);
        
        log.info("Successfully activated social network: {}", entity.getNetworkCode());
    }
    
    @Override
    public void deactivate(Long id) {
        log.info("Deactivating social network with ID: {}", id);
        SocialNetwork entity = repository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Social network not found with ID: " + id));
        
        entity.setIsActive(false);
        entity.setUpdatedAt(LocalDateTime.now());
        repository.save(entity);
        
        log.info("Successfully deactivated social network: {}", entity.getNetworkCode());
    }
    
    @Override
    public void enableApproval(Long id) {
        log.info("Enabling approval requirement for social network with ID: {}", id);
        SocialNetwork entity = repository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Social network not found with ID: " + id));
        
        entity.setRequiresApproval(true);
        entity.setUpdatedAt(LocalDateTime.now());
        repository.save(entity);
        
        log.info("Successfully enabled approval for social network: {}", entity.getNetworkCode());
    }
    
    @Override
    public void disableApproval(Long id) {
        log.info("Disabling approval requirement for social network with ID: {}", id);
        SocialNetwork entity = repository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Social network not found with ID: " + id));
        
        entity.setRequiresApproval(false);
        entity.setUpdatedAt(LocalDateTime.now());
        repository.save(entity);
        
        log.info("Successfully disabled approval for social network: {}", entity.getNetworkCode());
    }
    
    @Override
    public void updateSortOrder(Long id, Integer sortOrder) {
        log.info("Updating sort order for social network with ID: {} to {}", id, sortOrder);
        SocialNetwork entity = repository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Social network not found with ID: " + id));
        
        entity.setSortOrder(sortOrder);
        entity.setUpdatedAt(LocalDateTime.now());
        repository.save(entity);
        
        log.info("Successfully updated sort order for social network: {}", entity.getNetworkCode());
    }
    
    @Override
    public void updateApiConfig(Long id, String apiConfig) {
        log.info("Updating API configuration for social network with ID: {}", id);
        SocialNetwork entity = repository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Social network not found with ID: " + id));
        
        // Parse JSON string to Map
        try {
            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> configMap = mapper.readValue(apiConfig, Map.class);
            entity.setApiConfig(configMap);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid JSON format for API configuration", e);
        }
        
        entity.setUpdatedAt(LocalDateTime.now());
        repository.save(entity);
        
        log.info("Successfully updated API config for social network: {}", entity.getNetworkCode());
    }
    
    @Override
    public void updateRateLimits(Long id, String rateLimits) {
        log.info("Updating rate limits for social network with ID: {}", id);
        SocialNetwork entity = repository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Social network not found with ID: " + id));
        
        // Parse JSON string to Map
        try {
            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> limitsMap = mapper.readValue(rateLimits, Map.class);
            entity.setRateLimits(limitsMap);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid JSON format for rate limits", e);
        }
        
        entity.setUpdatedAt(LocalDateTime.now());
        repository.save(entity);
        
        log.info("Successfully updated rate limits for social network: {}", entity.getNetworkCode());
    }
    
    @Override
    public void updateSupportedFeatures(Long id, String supportedFeatures) {
        log.info("Updating supported features for social network with ID: {}", id);
        SocialNetwork entity = repository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Social network not found with ID: " + id));
        
        // Parse JSON string to Map
        try {
            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> featuresMap = mapper.readValue(supportedFeatures, Map.class);
            entity.setSupportedFeatures(featuresMap);
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid JSON format for supported features", e);
        }
        
        entity.setUpdatedAt(LocalDateTime.now());
        repository.save(entity);
        
        log.info("Successfully updated supported features for social network: {}", entity.getNetworkCode());
    }
    
    @Override
    public void delete(Long id) {
        log.info("Soft deleting social network with ID: {}", id);
        // Soft delete by deactivating
        deactivate(id);
        log.info("Successfully soft deleted social network with ID: {}", id);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long getTotalCount() {
        return repository.count();
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long getActiveCount() {
        return repository.countActiveNetworks();
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long getApprovalRequiredCount() {
        return repository.countNetworksByApprovalRequirement(true);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long getProductionReadyCount() {
        return (long) repository.findProductionReadyNetworks().size();
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean existsByNetworkCode(String networkCode) {
        return repository.existsByNetworkCode(networkCode);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean isNetworkActive(String networkCode) {
        return repository.findByNetworkCode(networkCode)
                .map(SocialNetwork::getIsActive)
                .orElse(false);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean supportsFeature(String networkCode, String feature) {
        return repository.findByNetworkCode(networkCode)
                .map(network -> network.supportsFeature(feature))
                .orElse(false);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Integer getRateLimit(String networkCode, String limitType) {
        return repository.findByNetworkCode(networkCode)
                .map(network -> network.getRateLimit(limitType))
                .orElse(null);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean validateApiConfig(String networkCode) {
        return repository.findByNetworkCode(networkCode)
                .map(network -> network.getApiConfig() != null && !network.getApiConfig().isEmpty())
                .orElse(false);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean testConnection(String networkCode) {
        // This is a placeholder for actual connection testing
        // In a real implementation, this would make actual API calls to test connectivity
        log.info("Testing connection for network: {}", networkCode);
        
        Optional<SocialNetwork> networkOpt = repository.findByNetworkCode(networkCode);
        if (networkOpt.isEmpty()) {
            return false;
        }
        
        SocialNetwork network = networkOpt.get();
        if (!network.getIsActive() || network.getApiConfig() == null || network.getApiConfig().isEmpty()) {
            return false;
        }
        
        // TODO: Implement actual API connectivity test based on network type
        // For now, return true if network is active and has valid config
        return true;
    }
}
