package service

import (
	"context"

	"eslogad-be/internal/contract"
	"eslogad-be/internal/entities"
	"eslogad-be/internal/repository"
	"eslogad-be/internal/transformer"

	"github.com/google/uuid"
)

type RBACServiceImpl struct {
	repo *repository.RBACRepository
}

func NewRBACService(repo *repository.RBACRepository) *RBACServiceImpl {
	return &RBACServiceImpl{repo: repo}
}

// Permissions
func (s *RBACServiceImpl) CreatePermission(ctx context.Context, req *contract.CreatePermissionRequest) (*contract.PermissionResponse, error) {
	p := &entities.Permission{Code: req.Code}
	if req.Description != nil {
		p.Description = *req.Description
	}
	if err := s.repo.CreatePermission(ctx, p); err != nil {
		return nil, err
	}
	return &contract.PermissionResponse{
		ID:          p.ID,
		Code:        p.Code,
		Action:      p.Action,
		Description: &p.Description,
	}, nil
}
func (s *RBACServiceImpl) UpdatePermission(ctx context.Context, id uuid.UUID, req *contract.UpdatePermissionRequest) (*contract.PermissionResponse, error) {
	p := &entities.Permission{ID: id}
	if req.Code != nil {
		p.Code = *req.Code
	}
	if req.Description != nil {
		p.Description = *req.Description
	}
	if err := s.repo.UpdatePermission(ctx, p); err != nil {
		return nil, err
	}
	// fetch full row
	perms, err := s.repo.ListPermissions(ctx)
	if err != nil {
		return nil, err
	}
	for _, x := range perms {
		if x.ID == id {
			return &contract.PermissionResponse{
				ID:          x.ID,
				Code:        x.Code,
				Action:      x.Action,
				Description: &x.Description,
			}, nil
		}
	}
	return nil, nil
}
func (s *RBACServiceImpl) DeletePermission(ctx context.Context, id uuid.UUID) error {
	return s.repo.DeletePermission(ctx, id)
}
func (s *RBACServiceImpl) ListPermissions(ctx context.Context) (*contract.ListPermissionsResponse, error) {
	perms, err := s.repo.ListPermissions(ctx)
	if err != nil {
		return nil, err
	}
	return &contract.ListPermissionsResponse{Permissions: transformer.PermissionsToContract(perms)}, nil
}

// Roles
func (s *RBACServiceImpl) CreateRole(ctx context.Context, req *contract.CreateRoleRequest) (*contract.RoleWithPermissionsResponse, error) {
	role := &entities.Role{Name: req.Name, Code: req.Code}
	if req.Description != nil {
		role.Description = *req.Description
	}
	if err := s.repo.CreateRole(ctx, role); err != nil {
		return nil, err
	}
	if len(req.PermissionCodes) > 0 {
		_ = s.repo.SetRolePermissionsByCodes(ctx, role.ID, req.PermissionCodes)
	}
	perms, _ := s.repo.GetPermissionsByRoleID(ctx, role.ID)
	resp := transformer.RoleWithPermissionsToContract(*role, perms)
	return &resp, nil
}
func (s *RBACServiceImpl) UpdateRole(ctx context.Context, id uuid.UUID, req *contract.UpdateRoleRequest) (*contract.RoleWithPermissionsResponse, error) {
	role := &entities.Role{ID: id}
	if req.Name != nil {
		role.Name = *req.Name
	}
	if req.Code != nil {
		role.Code = *req.Code
	}
	if req.Description != nil {
		role.Description = *req.Description
	}
	if err := s.repo.UpdateRole(ctx, role); err != nil {
		return nil, err
	}
	if req.PermissionCodes != nil {
		_ = s.repo.SetRolePermissionsByCodes(ctx, id, *req.PermissionCodes)
	}
	perms, _ := s.repo.GetPermissionsByRoleID(ctx, id)
	// fetch updated role
	roles, err := s.repo.ListRoles(ctx)
	if err != nil {
		return nil, err
	}
	for _, r := range roles {
		if r.ID == id {
			resp := transformer.RoleWithPermissionsToContract(r, perms)
			return &resp, nil
		}
	}
	return nil, nil
}
func (s *RBACServiceImpl) DeleteRole(ctx context.Context, id uuid.UUID) error {
	return s.repo.DeleteRole(ctx, id)
}
func (s *RBACServiceImpl) ListRoles(ctx context.Context) (*contract.ListRolesResponse, error) {
	roles, err := s.repo.ListRoles(ctx)
	if err != nil {
		return nil, err
	}
	out := make([]contract.RoleWithPermissionsResponse, 0, len(roles))
	for _, r := range roles {
		perms, _ := s.repo.GetPermissionsByRoleID(ctx, r.ID)
		out = append(out, transformer.RoleWithPermissionsToContract(r, perms))
	}
	return &contract.ListRolesResponse{Roles: out}, nil
}

// New methods for the required API endpoints
func (s *RBACServiceImpl) GetPermissionsGrouped(ctx context.Context) (*contract.PermissionsGroupedResponse, error) {
	modules, err := s.repo.ListModules(ctx)
	if err != nil {
		return nil, err
	}

	result := make([]contract.ModuleWithPermissionsResponse, 0, len(modules))
	for _, module := range modules {
		perms, err := s.repo.ListPermissions(ctx)
		if err != nil {
			return nil, err
		}

		modulePerms := make([]contract.PermissionResponse, 0)
		for _, perm := range perms {
			if perm.ModuleID != nil && *perm.ModuleID == module.ID {
				modulePerms = append(modulePerms, contract.PermissionResponse{
					ID:          perm.ID,
					Code:        perm.Code,
					Action:      perm.Action,
					Description: &perm.Description,
				})
			}
		}

		result = append(result, contract.ModuleWithPermissionsResponse{
			Module: contract.ModuleResponse{
				ID:   module.ID,
				Name: module.Name,
				Code: module.Code,
			},
			Permissions: modulePerms,
		})
	}

	return &contract.PermissionsGroupedResponse{Data: result}, nil
}

func (s *RBACServiceImpl) CreateOrUpdateRole(ctx context.Context, req *contract.CreateOrUpdateRoleRequest) (*contract.RoleDetailResponse, error) {
	// Check if role exists
	existingRole, _ := s.repo.GetRoleByCode(ctx, req.Code)

	var role *entities.Role
	if existingRole != nil {
		// Update existing role
		role = existingRole
		role.Name = req.Name
		role.Description = req.Description
		if err := s.repo.UpdateRole(ctx, role); err != nil {
			return nil, err
		}
	} else {
		// Create new role
		role = &entities.Role{
			Name:        req.Name,
			Code:        req.Code,
			Description: req.Description,
		}
		if err := s.repo.CreateRole(ctx, role); err != nil {
			return nil, err
		}
	}

	// Set permissions based on module and actions
	permissionIDs := make([]uuid.UUID, 0)
	for _, modPerm := range req.Permissions {
		_, err := s.repo.GetModuleByCode(ctx, modPerm.Module)
		if err != nil {
			continue // Skip if module not found
		}

		for _, action := range modPerm.Actions {
			permCode := modPerm.Module + "_" + action
			perm, err := s.repo.GetPermissionByCode(ctx, permCode)
			if err == nil && perm != nil {
				permissionIDs = append(permissionIDs, perm.ID)
			}
		}
	}

	if err := s.repo.SetRolePermissionsByIDs(ctx, role.ID, permissionIDs); err != nil {
		return nil, err
	}

	// Build response
	return s.GetRoleDetail(ctx, role.ID)
}

func (s *RBACServiceImpl) GetRoleDetail(ctx context.Context, roleID uuid.UUID) (*contract.RoleDetailResponse, error) {
	role, err := s.repo.GetRoleByID(ctx, roleID)
	if err != nil {
		return nil, err
	}

	permissions, err := s.repo.GetPermissionsByRoleID(ctx, roleID)
	if err != nil {
		return nil, err
	}

	// Group permissions by module
	moduleMap := make(map[uuid.UUID]*contract.RolePermissionModuleResponse)

	for _, perm := range permissions {
		if perm.ModuleID == nil {
			continue
		}

		if _, exists := moduleMap[*perm.ModuleID]; !exists {
			if perm.Module != nil {
				moduleMap[*perm.ModuleID] = &contract.RolePermissionModuleResponse{
					Module: contract.ModuleResponse{
						ID:   perm.Module.ID,
						Name: perm.Module.Name,
						Code: perm.Module.Code,
					},
					Actions: []contract.PermissionActionResponse{},
				}
			}
		}

		if modResp, exists := moduleMap[*perm.ModuleID]; exists {
			modResp.Actions = append(modResp.Actions, contract.PermissionActionResponse{
				ID:          perm.ID,
				Action:      perm.Action,
				Code:        perm.Code,
				Description: perm.Description,
			})
		}
	}

	// Convert map to slice
	permissionModules := make([]contract.RolePermissionModuleResponse, 0, len(moduleMap))
	for _, modResp := range moduleMap {
		permissionModules = append(permissionModules, *modResp)
	}

	return &contract.RoleDetailResponse{
		ID:          role.ID,
		Name:        role.Name,
		Code:        role.Code,
		Description: role.Description,
		CreatedAt:   role.CreatedAt,
		UpdatedAt:   role.UpdatedAt,
		Permissions: permissionModules,
	}, nil
}
