package service import ( "aggios-app/backend/internal/config" "aggios-app/backend/internal/domain" "aggios-app/backend/internal/repository" "fmt" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" ) // AgencyService handles agency registration and management type AgencyService struct { userRepo *repository.UserRepository tenantRepo *repository.TenantRepository cfg *config.Config } // NewAgencyService creates a new agency service func NewAgencyService(userRepo *repository.UserRepository, tenantRepo *repository.TenantRepository, cfg *config.Config) *AgencyService { return &AgencyService{ userRepo: userRepo, tenantRepo: tenantRepo, cfg: cfg, } } // RegisterAgency creates a new agency (tenant) and its admin user // Only SUPERADMIN can call this func (s *AgencyService) RegisterAgency(req domain.RegisterAgencyRequest) (*domain.Tenant, *domain.User, error) { // Validate password if len(req.AdminPassword) < s.cfg.Security.PasswordMinLength { return nil, nil, ErrWeakPassword } // Check if subdomain is available exists, err := s.tenantRepo.SubdomainExists(req.Subdomain) if err != nil { return nil, nil, err } if exists { return nil, nil, ErrSubdomainTaken } // Check if admin email already exists emailExists, err := s.userRepo.EmailExists(req.AdminEmail) if err != nil { return nil, nil, err } if emailExists { return nil, nil, ErrEmailAlreadyExists } // Create tenant address := req.Street if req.Number != "" { address += ", " + req.Number } if req.Complement != "" { address += " - " + req.Complement } if req.Neighborhood != "" { address += " - " + req.Neighborhood } tenant := &domain.Tenant{ Name: req.AgencyName, Domain: fmt.Sprintf("%s.%s", req.Subdomain, s.cfg.App.BaseDomain), Subdomain: req.Subdomain, CNPJ: req.CNPJ, RazaoSocial: req.RazaoSocial, Email: req.AdminEmail, Website: req.Website, Address: address, City: req.City, State: req.State, Zip: req.CEP, Description: req.Description, Industry: req.Industry, } if err := s.tenantRepo.Create(tenant); err != nil { return nil, nil, err } // Hash password hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.AdminPassword), bcrypt.DefaultCost) if err != nil { return nil, nil, err } // Create admin user for the agency adminUser := &domain.User{ TenantID: &tenant.ID, Email: req.AdminEmail, Password: string(hashedPassword), Name: req.AdminName, Role: "ADMIN_AGENCIA", } if err := s.userRepo.Create(adminUser); err != nil { return nil, nil, err } return tenant, adminUser, nil } // RegisterClient creates a new client user for a specific agency // Only ADMIN_AGENCIA can call this func (s *AgencyService) RegisterClient(req domain.RegisterClientRequest, tenantID uuid.UUID) (*domain.User, error) { // Validate password if len(req.Password) < s.cfg.Security.PasswordMinLength { return nil, ErrWeakPassword } // Check if email already exists exists, err := s.userRepo.EmailExists(req.Email) if err != nil { return nil, err } if exists { return nil, ErrEmailAlreadyExists } // Hash password hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { return nil, err } // Create client user client := &domain.User{ TenantID: &tenantID, Email: req.Email, Password: string(hashedPassword), Name: req.Name, Role: "CLIENTE", } if err := s.userRepo.Create(client); err != nil { return nil, err } return client, nil } // GetAgencyDetails returns tenant and admin information for superadmin view func (s *AgencyService) GetAgencyDetails(id uuid.UUID) (*domain.AgencyDetails, error) { tenant, err := s.tenantRepo.FindByID(id) if err != nil { return nil, err } if tenant == nil { return nil, ErrTenantNotFound } admin, err := s.userRepo.FindAdminByTenantID(id) if err != nil { return nil, err } protocol := "http://" if s.cfg.App.Environment == "production" { protocol = "https://" } details := &domain.AgencyDetails{ Tenant: tenant, AccessURL: fmt.Sprintf("%s%s", protocol, tenant.Domain), } if admin != nil { details.Admin = admin } return details, nil } // DeleteAgency removes a tenant and its related resources func (s *AgencyService) DeleteAgency(id uuid.UUID) error { tenant, err := s.tenantRepo.FindByID(id) if err != nil { return err } if tenant == nil { return ErrTenantNotFound } return s.tenantRepo.Delete(id) }