package com.saas.admin.service;

import com.saas.admin.entity.Permission;
import com.saas.admin.repository.PermissionRepository;
import com.saas.admin.repository.RolePermissionRepository;
import com.saas.shared.audit.Auditable;
import com.saas.shared.security.PermissionCacheService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

/**
 * Service for managing Permissions in the RBAC system
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class PermissionService {
    
    private final PermissionRepository permissionRepository;
    private final RolePermissionRepository rolePermissionRepository;
    private final PermissionCacheService permissionCacheService;
    
    /**
     * Create a new permission
     * @param resource Resource name (e.g., "tenant", "user", "voip")
     * @param action Action name (e.g., "read", "write", "delete")
     * @param description Human-readable description
     * @param isSystem Whether this is a system permission
     */
    @Transactional
    @Auditable(action = "CREATE_PERMISSION", entityType = "PERMISSION")
    public Permission createPermission(String resource, String action, String description, boolean isSystem) {
        log.info("📝 Creating permission: {}:{}", resource, action);
        
        String normalizedResource = resource.toLowerCase();
        String normalizedAction = action.toLowerCase();
        
        if (permissionRepository.existsByResourceAndAction(normalizedResource, normalizedAction)) {
            throw new IllegalArgumentException("Permission '" + normalizedResource + ":" + normalizedAction + "' already exists");
        }
        
        Permission permission = Permission.builder()
                .resource(normalizedResource)
                .action(normalizedAction)
                .description(description)
                .isActive(true)
                .isSystem(isSystem)
                .build();
        
        Permission saved = permissionRepository.save(permission);
        log.info("✅ Permission created with ID: {}", saved.getId());
        
        return saved;
    }
    
    /**
     * Create permission from string format "resource:action"
     */
    @Transactional
    public Permission createPermissionFromString(String permissionString, String description, boolean isSystem) {
        Permission permission = Permission.fromString(permissionString, description);
        permission.setIsSystem(isSystem);
        
        return createPermission(permission.getResource(), permission.getAction(), description, isSystem);
    }
    
    /**
     * Get permission by ID
     */
    @Transactional(readOnly = true)
    public Optional<Permission> getPermissionById(Long id) {
        return permissionRepository.findById(id);
    }
    
    /**
     * Get permission by resource and action
     */
    @Transactional(readOnly = true)
    public Optional<Permission> getPermissionByResourceAndAction(String resource, String action) {
        return permissionRepository.findByResourceAndAction(resource.toLowerCase(), action.toLowerCase());
    }
    
    /**
     * Get all permissions
     */
    @Transactional(readOnly = true)
    public List<Permission> getAllPermissions() {
        return permissionRepository.findAll();
    }
    
    /**
     * Get all active permissions
     */
    @Transactional(readOnly = true)
    public List<Permission> getAllActivePermissions() {
        return permissionRepository.findByIsActiveTrue();
    }
    
    /**
     * Get all permissions for a resource
     */
    @Transactional(readOnly = true)
    public List<Permission> getPermissionsByResource(String resource) {
        return permissionRepository.findByResource(resource.toLowerCase());
    }
    
    /**
     * Get all distinct resources
     */
    @Transactional(readOnly = true)
    public List<String> getAllResources() {
        return permissionRepository.findAllResources();
    }
    
    /**
     * Get all distinct actions
     */
    @Transactional(readOnly = true)
    public List<String> getAllActions() {
        return permissionRepository.findAllActions();
    }
    
    /**
     * Update permission
     */
    @Transactional
    @Auditable(action = "UPDATE_PERMISSION", entityType = "PERMISSION")
    public Permission updatePermission(Long id, String description, Boolean isActive) {
        log.info("Updating permission ID: {}", id);
        
        Permission permission = permissionRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Permission not found with ID: " + id));
        
        if (description != null) {
            permission.setDescription(description);
        }
        
        if (isActive != null) {
            permission.setIsActive(isActive);
            permissionCacheService.invalidateByPermission(id);
        }
        
        Permission updated = permissionRepository.save(permission);
        log.info("Permission updated");
        
        return updated;
    }
    
    /**
     * Delete permission (only if not system permission and not assigned to any role)
     */
    @Transactional
    @Auditable(action = "DELETE_PERMISSION", entityType = "PERMISSION")
    public void deletePermission(Long id) {
        log.info("Deleting permission ID: {}", id);
        
        Permission permission = permissionRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("Permission not found with ID: " + id));
        
        if (permission.getIsSystem()) {
            throw new IllegalStateException("Cannot delete system permission: " + permission.getPermissionString());
        }
        
        // Check if permission is assigned to any roles
        long roleCount = rolePermissionRepository.findByPermission(permission).size();
        if (roleCount > 0) {
            throw new IllegalStateException("Cannot delete permission assigned to " + roleCount + " roles. Remove role assignments first.");
        }
        
        permissionRepository.delete(permission);
        
        permissionCacheService.invalidateByPermission(id);
        
        log.info("Permission deleted");
    }
    
    /**
     * Bulk create permissions from list of strings
     * Example: ["tenant:read", "tenant:write", "user:read"]
     */
    @Transactional
    public List<Permission> bulkCreatePermissions(List<String> permissionStrings, boolean isSystem) {
        log.info("📝 Bulk creating {} permissions", permissionStrings.size());
        
        return permissionStrings.stream()
                .map(permString -> {
                    try {
                        Permission perm = Permission.fromString(permString, "Auto-created permission");
                        perm.setIsSystem(isSystem);
                        
                        // Check if exists
                        Optional<Permission> existing = permissionRepository
                                .findByResourceAndAction(perm.getResource(), perm.getAction());
                        
                        if (existing.isPresent()) {
                            log.debug("⚠️ Permission already exists: {}", permString);
                            return existing.get();
                        }
                        
                        return permissionRepository.save(perm);
                    } catch (Exception e) {
                        log.error("❌ Failed to create permission: {}", permString, e);
                        return null;
                    }
                })
                .filter(java.util.Objects::nonNull)
                .toList();
    }
}
