package com.social.media.service.impl;

import com.social.media.dto.SocialAccountDto;
import com.social.media.domain.entity.SocialAccount;
import com.social.media.repository.SocialAccountRepository;
import com.social.media.service.SocialAccountService;
import com.social.media.domain.shared.PageResponse;
import com.social.media.util.SocialAccountMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.stream.Collectors;

@Service
@Transactional
public class SocialAccountServiceImpl implements SocialAccountService {
    
    @Autowired
    private SocialAccountRepository socialAccountRepository;
    
    // CRUD Operations
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getAllSocialAccounts(int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByDeletedFalse(pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public SocialAccountDto getSocialAccountById(Long id) {
        SocialAccount account = socialAccountRepository.findByIdAndDeletedFalse(id)
                .orElseThrow(() -> new RuntimeException("Social Account not found with id: " + id));
        return SocialAccountMapper.toDto(account);
    }
    
    @Override
    public SocialAccountDto createSocialAccount(SocialAccountDto socialAccountDto) {
        if (existsByCompanyAndSocialNetworkAndUsername(
                socialAccountDto.getCompanyId(), 
                socialAccountDto.getSocialNetworkId(), 
                socialAccountDto.getUsername())) {
            throw new RuntimeException("Account already exists for this username and social network");
        }
        
        SocialAccount account = SocialAccountMapper.toEntity(socialAccountDto);
        account.setCreatedAt(LocalDateTime.now());
        account.setUpdatedAt(LocalDateTime.now());
        account.setDeleted(false);
        
        SocialAccount savedAccount = socialAccountRepository.save(account);
        return SocialAccountMapper.toDto(savedAccount);
    }
    
    @Override
    public SocialAccountDto updateSocialAccount(Long id, SocialAccountDto socialAccountDto) {
        SocialAccount existingAccount = socialAccountRepository.findByIdAndDeletedFalse(id)
                .orElseThrow(() -> new RuntimeException("Social Account not found with id: " + id));
        
        // Update fields
        existingAccount.setDisplayName(socialAccountDto.getDisplayName());
        existingAccount.setProfileUrl(socialAccountDto.getProfileUrl());
        existingAccount.setProfilePhotoUrl(socialAccountDto.getAvatarUrl()); // Entity uses profilePhotoUrl
        existingAccount.setBio(socialAccountDto.getBio());
        existingAccount.setLocation(socialAccountDto.getLocation());
        existingAccount.setWebsite(socialAccountDto.getWebsite());
        existingAccount.setActive(socialAccountDto.getActive());
        existingAccount.setVerified(socialAccountDto.getVerified());
        existingAccount.setIsPrivate(socialAccountDto.getIsPrivate());
        existingAccount.setAccountType(socialAccountDto.getAccountType());
        existingAccount.setBusinessCategory(socialAccountDto.getBusinessCategory());
        existingAccount.setResponsibleUserId(socialAccountDto.getResponsibleUserId());
        existingAccount.setFollowerTarget(socialAccountDto.getFollowerTarget());
        // Convert Double to BigDecimal for engagement target
        existingAccount.setEngagementTarget(socialAccountDto.getEngagementTarget() != null ? 
            java.math.BigDecimal.valueOf(socialAccountDto.getEngagementTarget()) : null);
        // existingAccount.setAdditionalData(socialAccountDto.getAdditionalData()); // Will add this field later
        existingAccount.setUpdatedAt(LocalDateTime.now());
        
        SocialAccount updatedAccount = socialAccountRepository.save(existingAccount);
        return SocialAccountMapper.toDto(updatedAccount);
    }
    
    @Override
    public void deleteSocialAccount(Long id) {
        SocialAccount account = socialAccountRepository.findByIdAndDeletedFalse(id)
                .orElseThrow(() -> new RuntimeException("Social Account not found with id: " + id));
        
        account.setDeleted(true);
        account.setUpdatedAt(LocalDateTime.now());
        socialAccountRepository.save(account);
    }
    
    // Company-based queries
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getAccountsByCompany(Long companyId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByCompanyIdAndDeletedFalse(companyId, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getActiveAccountsByCompany(Long companyId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByCompanyIdAndActiveTrueAndDeletedFalse(companyId, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getConnectedAccountsByCompany(Long companyId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByCompanyIdAndConnectionStatusAndDeletedFalse(
                companyId, SocialAccount.ConnectionStatus.CONNECTED, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    // Social network queries
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getAccountsBySocialNetwork(Long socialNetworkId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findBySocialNetworkIdAndDeletedFalse(socialNetworkId, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    // User-based queries
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getAccountsByResponsibleUser(Long responsibleUserId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByResponsibleUserIdAndDeletedFalse(responsibleUserId, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    // Search and filtering
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> searchAccountsByUsername(String username, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByUsernameContainingIgnoreCase(username, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> searchAccountsByNameOrUsername(Long companyId, String searchTerm, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.searchAccountsByNameOrUsername(companyId, searchTerm, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getAccountsByConnectionStatus(Long companyId, SocialAccount.ConnectionStatus status, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByCompanyIdAndConnectionStatusAndDeletedFalse(companyId, status, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getAccountsByEngagementRange(Double minEngagement, Double maxEngagement, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByEngagementRateBetween(minEngagement, maxEngagement, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getAccountsByFollowersRange(Integer minFollowers, Integer maxFollowers, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByFollowersCountBetween(minFollowers, maxFollowers, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    // Analytics and metrics
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getTopAccountsByFollowers(Long companyId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        Page<SocialAccount> accountsPage = socialAccountRepository.findTopAccountsByFollowers(companyId, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getTopAccountsByEngagement(Long companyId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        Page<SocialAccount> accountsPage = socialAccountRepository.findTopAccountsByEngagement(companyId, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long getTotalFollowersByCompany(Long companyId) {
        return socialAccountRepository.getTotalFollowersByCompany(companyId);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Double getAverageEngagementByCompany(Long companyId) {
        return socialAccountRepository.getAverageEngagementByCompany(companyId);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialAccountDto> getAccountsReachingFollowerTargets() {
        List<SocialAccount> accounts = socialAccountRepository.findAccountsReachingFollowerTargets();
        return accounts.stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialAccountDto> getAccountsReachingEngagementTargets() {
        List<SocialAccount> accounts = socialAccountRepository.findAccountsReachingEngagementTargets();
        return accounts.stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<Object[]> getConnectedAccountsSummaryByCompany(Long companyId) {
        return socialAccountRepository.getConnectedAccountsSummaryByCompany(companyId);
    }
    
    // Account management
    @Override
    public SocialAccountDto connectAccount(Long id, Map<String, Object> connectionData) {
        SocialAccount account = socialAccountRepository.findByIdAndDeletedFalse(id)
                .orElseThrow(() -> new RuntimeException("Social Account not found with id: " + id));
        
        account.setConnectionStatus(SocialAccount.ConnectionStatus.CONNECTED);
        account.setLastSyncDate(LocalDateTime.now());
        account.setNextSyncDate(LocalDateTime.now().plusHours(24)); // Default sync interval
        // account.setAdditionalData(connectionData); // Will add this field later
        account.setUpdatedAt(LocalDateTime.now());
        
        SocialAccount updatedAccount = socialAccountRepository.save(account);
        return SocialAccountMapper.toDto(updatedAccount);
    }
    
    @Override
    public SocialAccountDto disconnectAccount(Long id) {
        SocialAccount account = socialAccountRepository.findByIdAndDeletedFalse(id)
                .orElseThrow(() -> new RuntimeException("Social Account not found with id: " + id));
        
        account.setConnectionStatus(SocialAccount.ConnectionStatus.DISCONNECTED);
        account.setActive(false);
        account.setUpdatedAt(LocalDateTime.now());
        
        SocialAccount updatedAccount = socialAccountRepository.save(account);
        return SocialAccountMapper.toDto(updatedAccount);
    }
    
    @Override
    public SocialAccountDto syncAccount(Long id) {
        SocialAccount account = socialAccountRepository.findByIdAndDeletedFalse(id)
                .orElseThrow(() -> new RuntimeException("Social Account not found with id: " + id));
        
        if (account.getConnectionStatus() != SocialAccount.ConnectionStatus.CONNECTED) {
            throw new RuntimeException("Account must be connected to sync");
        }
        
        // Here you would implement actual sync logic with the social media platform
        account.setLastSyncDate(LocalDateTime.now());
        account.setNextSyncDate(LocalDateTime.now().plusHours(24));
        account.setLastSyncError(null); // Clear any previous errors
        account.setUpdatedAt(LocalDateTime.now());
        
        SocialAccount updatedAccount = socialAccountRepository.save(account);
        return SocialAccountMapper.toDto(updatedAccount);
    }
    
    @Override
    public List<SocialAccountDto> syncAccountsForCompany(Long companyId) {
        List<SocialAccount> connectedAccounts = socialAccountRepository.findByCompanyIdAndConnectionStatusAndDeletedFalse(
                companyId, SocialAccount.ConnectionStatus.CONNECTED, PageRequest.of(0, Integer.MAX_VALUE)).getContent();
        
        return connectedAccounts.stream()
                .map(account -> syncAccount(account.getId()))
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialAccountDto> getAccountsNeedingSync() {
        List<SocialAccount> accounts = socialAccountRepository.findAccountsNeedingSync(LocalDateTime.now());
        return accounts.stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
    }
    
    // Validation and checks
    @Override
    @Transactional(readOnly = true)
    public boolean existsByAccountCode(String accountCode) {
        return socialAccountRepository.existsByAccountCode(accountCode);
    }
    
    @Override
    @Transactional(readOnly = true)
    public boolean existsByCompanyAndSocialNetworkAndUsername(Long companyId, Long socialNetworkId, String username) {
        return socialAccountRepository.existsByCompanyIdAndSocialNetworkIdAndUsernameAndDeletedFalse(companyId, socialNetworkId, username);
    }
    
    // Metrics and performance
    @Override
    public SocialAccountDto updateAccountMetrics(Long id, Map<String, Object> metrics) {
        SocialAccount account = socialAccountRepository.findByIdAndDeletedFalse(id)
                .orElseThrow(() -> new RuntimeException("Social Account not found with id: " + id));
        
        account.setMetrics(metrics);
        account.setLastMetricsUpdate(LocalDateTime.now());
        account.setUpdatedAt(LocalDateTime.now());
        
        SocialAccount updatedAccount = socialAccountRepository.save(account);
        return SocialAccountMapper.toDto(updatedAccount);
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialAccountDto> getLowPerformingAccounts() {
        List<SocialAccount> accounts = socialAccountRepository.findLowPerformingAccounts();
        return accounts.stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
    }
    
    @Override
    @Transactional(readOnly = true)
    public List<SocialAccountDto> getAccountsWithStaleMetrics(LocalDateTime cutoffDate) {
        List<SocialAccount> accounts = socialAccountRepository.findAccountsWithStaleMetrics(cutoffDate);
        return accounts.stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
    }
    
    // Filtering by account properties
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getVerifiedAccounts(int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByVerifiedAndDeletedFalse(true, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getBusinessAccounts(Long companyId, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findBusinessAccountsByCompany(companyId, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getAccountsByType(String accountType, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByAccountTypeAndDeletedFalse(accountType, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getAccountsByCategory(String businessCategory, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByBusinessCategoryAndDeletedFalse(businessCategory, pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    @Override
    @Transactional(readOnly = true)
    public PageResponse<SocialAccountDto> getAccountsWithSyncErrors(int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        Page<SocialAccount> accountsPage = socialAccountRepository.findByLastSyncErrorIsNotNullAndDeletedFalse(pageable);
        
        List<SocialAccountDto> accountDtos = accountsPage.getContent().stream()
                .map(SocialAccountMapper::toDto)
                .collect(Collectors.toList());
        
        return PageResponse.of(accountDtos, (int) accountsPage.getTotalElements(), page, size);
    }
    
    // Statistics
    @Override
    @Transactional(readOnly = true)
    public Long countAccountsByCompany(Long companyId) {
        return socialAccountRepository.countAccountsByCompany(companyId);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long countActiveAccountsByCompany(Long companyId) {
        return socialAccountRepository.countActiveAccountsByCompany(companyId);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long countConnectedAccountsByCompany(Long companyId) {
        return socialAccountRepository.countConnectedAccountsByCompany(companyId);
    }
    
    @Override
    @Transactional(readOnly = true)
    public Long countAccountsBySocialNetwork(Long socialNetworkId) {
        return socialAccountRepository.countAccountsBySocialNetwork(socialNetworkId);
    }
}
