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, Description: &p.Description, CreatedAt: p.CreatedAt, UpdatedAt: p.UpdatedAt}, 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, Description: &x.Description, CreatedAt: x.CreatedAt, UpdatedAt: x.UpdatedAt}, 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
}
