package com.saas.admin.service;

import com.saas.admin.entity.CallerIdMapping;
import com.saas.admin.repository.CallerIdMappingRepository;
import com.saas.admin.repository.TenantRepository;
import com.saas.shared.audit.Auditable;
import com.saas.shared.exception.BusinessException;
import com.saas.shared.exception.ErrorCode;
import com.saas.shared.service.PhoneNumberBoundaryService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * CallerIdMappingService
 * 
 * Admin service for managing custom caller ID to tenant mappings.
 * Used when clients forward calls from their own numbers to our VoIP platform.
 * 
 * Security:
 * - All operations audited via @Auditable
 * - Validates tenant existence before creating mappings
 * - Enforces unique constraint: one caller number = one tenant only
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class CallerIdMappingService {

    private final CallerIdMappingRepository callerIdMappingRepository;
    private final TenantRepository tenantRepository;
    private final PhoneNumberBoundaryService phoneNumberBoundaryService;

    /**
     * Create a new caller ID mapping
     * 
     * @param fromPhoneNumber Caller's phone number (E.164 format)
     * @param tenantId Tenant ID to map to
     * @param description Human-readable description
     * @param createdBy Admin user ID creating this mapping
     * @return Created mapping entity
     */
    @Transactional
    @Auditable(action = "CREATE", entityType = "CallerIdMapping")
    public CallerIdMapping createMapping(String fromPhoneNumber, String tenantId, 
                                          String description, Long createdBy) {
        log.info("📞 [CallerIdMapping] Creating mapping: From={}, Tenant={}", fromPhoneNumber, tenantId);

        // Validate phone number format (basic E.164 check)
        if (!fromPhoneNumber.matches("^\\+?[1-9]\\d{1,14}$")) {
            throw new BusinessException(ErrorCode.INVALID_INPUT, 
                    "Invalid phone number format. Use E.164 format (e.g., +33612345678)");
        }

        // Check if mapping already exists
        if (callerIdMappingRepository.existsByFromPhoneNumber(fromPhoneNumber)) {
            throw new BusinessException(ErrorCode.VALIDATION_ERROR,
                "Caller ID mapping already exists for phone number: " + fromPhoneNumber);
        }

        // Validate tenant exists
        if (!tenantRepository.existsByTenantId(tenantId)) {
            throw new BusinessException(ErrorCode.TENANT_NOT_FOUND, 
                    "Tenant not found: " + tenantId);
        }

        CallerIdMapping mapping = CallerIdMapping.builder()
                .fromPhoneNumber(fromPhoneNumber)
                .tenantId(tenantId)
                .description(description)
                .createdBy(createdBy)
                .isActive(true)
                .build();

        CallerIdMapping saved = callerIdMappingRepository.save(mapping);

        // Evict cache to reflect new mapping immediately
        phoneNumberBoundaryService.evictPhoneNumberCache();

        log.info("✅ [CallerIdMapping] Mapping created successfully: ID={}", saved.getId());
        return saved;
    }

    /**
     * List all caller ID mappings for a tenant (paginated)
     */
    @Transactional(readOnly = true)
    public Page<CallerIdMapping> listMappingsByTenant(String tenantId, Pageable pageable) {
        return callerIdMappingRepository.findByTenantId(tenantId, pageable);
    }

    /**
     * Get a single mapping by ID
     */
    @Transactional(readOnly = true)
    public CallerIdMapping getMappingById(Long id) {
        return callerIdMappingRepository.findById(id)
            .orElseThrow(() -> new BusinessException(ErrorCode.VALIDATION_ERROR,
                "Caller ID mapping not found: " + id));
    }

    /**
     * Delete a caller ID mapping
     */
    @Transactional
    @Auditable(action = "DELETE", entityType = "CallerIdMapping")
    public void deleteMapping(Long id) {
        log.info("🗑️ [CallerIdMapping] Deleting mapping: ID={}", id);

        CallerIdMapping mapping = getMappingById(id);
        callerIdMappingRepository.delete(mapping);

        // Evict cache to remove deleted mapping
        phoneNumberBoundaryService.evictPhoneNumberCache();

        log.info("✅ [CallerIdMapping] Mapping deleted: ID={}", id);
    }

    /**
     * Toggle active status of a mapping
     */
    @Transactional
    @Auditable(action = "UPDATE", entityType = "CallerIdMapping")
    public CallerIdMapping toggleMappingStatus(Long id) {
        log.info("🔄 [CallerIdMapping] Toggling status: ID={}", id);

        CallerIdMapping mapping = getMappingById(id);
        mapping.setIsActive(!mapping.getIsActive());
        CallerIdMapping updated = callerIdMappingRepository.save(mapping);

        // Evict cache
        phoneNumberBoundaryService.evictPhoneNumberCache();

        log.info("✅ [CallerIdMapping] Status toggled: ID={}, Active={}", id, updated.getIsActive());
        return updated;
    }

    /**
     * Count mappings for a tenant (for quota checks)
     */
    @Transactional(readOnly = true)
    public long countMappingsByTenant(String tenantId) {
        return callerIdMappingRepository.countByTenantId(tenantId);
    }
}
