package repository import ( "database/sql" "time" "aggios-app/backend/internal/domain" "github.com/google/uuid" ) // TenantRepository handles database operations for tenants type TenantRepository struct { db *sql.DB } // NewTenantRepository creates a new tenant repository func NewTenantRepository(db *sql.DB) *TenantRepository { return &TenantRepository{db: db} } // Create creates a new tenant func (r *TenantRepository) Create(tenant *domain.Tenant) error { query := ` INSERT INTO tenants ( id, name, domain, subdomain, cnpj, razao_social, email, website, address, city, state, zip, description, industry, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) RETURNING id, created_at, updated_at ` now := time.Now() tenant.ID = uuid.New() tenant.CreatedAt = now tenant.UpdatedAt = now return r.db.QueryRow( query, tenant.ID, tenant.Name, tenant.Domain, tenant.Subdomain, tenant.CNPJ, tenant.RazaoSocial, tenant.Email, tenant.Website, tenant.Address, tenant.City, tenant.State, tenant.Zip, tenant.Description, tenant.Industry, tenant.CreatedAt, tenant.UpdatedAt, ).Scan(&tenant.ID, &tenant.CreatedAt, &tenant.UpdatedAt) } // FindByID finds a tenant by ID func (r *TenantRepository) FindByID(id uuid.UUID) (*domain.Tenant, error) { query := ` SELECT id, name, domain, subdomain, cnpj, razao_social, email, phone, website, address, city, state, zip, description, industry, is_active, created_at, updated_at FROM tenants WHERE id = $1 ` tenant := &domain.Tenant{} var cnpj, razaoSocial, email, phone, website, address, city, state, zip, description, industry sql.NullString err := r.db.QueryRow(query, id).Scan( &tenant.ID, &tenant.Name, &tenant.Domain, &tenant.Subdomain, &cnpj, &razaoSocial, &email, &phone, &website, &address, &city, &state, &zip, &description, &industry, &tenant.IsActive, &tenant.CreatedAt, &tenant.UpdatedAt, ) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, err } // Handle nullable fields if cnpj.Valid { tenant.CNPJ = cnpj.String } if razaoSocial.Valid { tenant.RazaoSocial = razaoSocial.String } if email.Valid { tenant.Email = email.String } if phone.Valid { tenant.Phone = phone.String } if website.Valid { tenant.Website = website.String } if address.Valid { tenant.Address = address.String } if city.Valid { tenant.City = city.String } if state.Valid { tenant.State = state.String } if zip.Valid { tenant.Zip = zip.String } if description.Valid { tenant.Description = description.String } if industry.Valid { tenant.Industry = industry.String } return tenant, nil } // FindBySubdomain finds a tenant by subdomain func (r *TenantRepository) FindBySubdomain(subdomain string) (*domain.Tenant, error) { query := ` SELECT id, name, domain, subdomain, created_at, updated_at FROM tenants WHERE subdomain = $1 ` tenant := &domain.Tenant{} err := r.db.QueryRow(query, subdomain).Scan( &tenant.ID, &tenant.Name, &tenant.Domain, &tenant.Subdomain, &tenant.CreatedAt, &tenant.UpdatedAt, ) if err == sql.ErrNoRows { return nil, nil } return tenant, err } // SubdomainExists checks if a subdomain is already taken func (r *TenantRepository) SubdomainExists(subdomain string) (bool, error) { var exists bool query := `SELECT EXISTS(SELECT 1 FROM tenants WHERE subdomain = $1)` err := r.db.QueryRow(query, subdomain).Scan(&exists) return exists, err } // FindAll returns all tenants func (r *TenantRepository) FindAll() ([]*domain.Tenant, error) { query := ` SELECT id, name, domain, subdomain, is_active, created_at, updated_at FROM tenants ORDER BY created_at DESC ` rows, err := r.db.Query(query) if err != nil { return nil, err } defer rows.Close() var tenants []*domain.Tenant for rows.Next() { tenant := &domain.Tenant{} err := rows.Scan( &tenant.ID, &tenant.Name, &tenant.Domain, &tenant.Subdomain, &tenant.IsActive, &tenant.CreatedAt, &tenant.UpdatedAt, ) if err != nil { return nil, err } tenants = append(tenants, tenant) } if tenants == nil { return []*domain.Tenant{}, nil } return tenants, nil } // Delete removes a tenant (and cascades to related data) func (r *TenantRepository) Delete(id uuid.UUID) error { result, err := r.db.Exec(`DELETE FROM tenants WHERE id = $1`, id) if err != nil { return err } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return sql.ErrNoRows } return nil } // UpdateProfile updates tenant profile information func (r *TenantRepository) UpdateProfile(id uuid.UUID, updates map[string]interface{}) error { query := ` UPDATE tenants SET name = COALESCE($1, name), cnpj = COALESCE($2, cnpj), razao_social = COALESCE($3, razao_social), email = COALESCE($4, email), phone = COALESCE($5, phone), website = COALESCE($6, website), address = COALESCE($7, address), city = COALESCE($8, city), state = COALESCE($9, state), zip = COALESCE($10, zip), description = COALESCE($11, description), industry = COALESCE($12, industry), updated_at = $13 WHERE id = $14 ` _, err := r.db.Exec( query, updates["name"], updates["cnpj"], updates["razao_social"], updates["email"], updates["phone"], updates["website"], updates["address"], updates["city"], updates["state"], updates["zip"], updates["description"], updates["industry"], time.Now(), id, ) return err }